wxWidgets Sizer does not automatically layout - wxwidgets

Here is my code:
cLicensePanel::cLicensePanel( wxWindow * parent )
: wxPanel(parent,-1,wxPoint(0,0),wxSize(500,500))
{
wxBoxSizer * szrUserKey = new wxBoxSizer(wxHORIZONTAL);
myUserKeyCtrl = new wxTextCtrl(this,-1,L"");
szrUserKey->Add( new wxStaticText(this,-1,wxString(L"User's ComputerKey:")),
0,wxALL,10);
szrUserKey->Add( myUserKeyCtrl,0,wxALL,10);
szrUserKey->Add( new wxButton(this,IDC_Generate,L"Generate"),0,wxALL,10);
SetSizer( szrUserKey );
}
This is intended to produce this display, with the 3 widgets neatly arranged in a row.
However, what I actually get is all the widgets piled one on top of the other
Why?
I can force the correct display by adding an explicit call to Sizer::Layout()
cLicensePanel::cLicensePanel( wxWindow * parent )
: wxPanel(parent,-1,wxPoint(0,0),wxSize(500,500))
{
wxBoxSizer * szrUserKey = new wxBoxSizer(wxHORIZONTAL);
myUserKeyCtrl = new wxTextCtrl(this,-1,L"");
szrUserKey->Add( new wxStaticText(this,-1,wxString(L"User's Computer Key:")),0,wxALL,10);
szrUserKey->Add( myUserKeyCtrl,0,wxALL,10);
szrUserKey->Add( new wxButton(this,IDC_Generate,L"Generate"),0,wxALL,10);
szrUserKey->Layout();
SetSizer( szrUserKey );
}
I can live with this, though it seems to me that it should be unnecessary to call Layout. I also suspect that the reason the Layout call is neccessary might be an important clue to the problem that has me stumped.
The above is the behaviour when using wxWidgets v2.8.12. I am upgrading to v2.9.3.
In the new version the initial code, without the call to Layout(), shows the same problem with the widgets all piled on top of each other. If I add the call to layout() in v2.9.3 then I cannot see any of the widgets at all - the panel shows completely blank!

SetSizer() intentionally doesn't do the layout, it only happens when the window is resized. But it also so happens that a newly created window always gets a EVT_SIZE, soon after being shown, so by the time the user sees it, it's already laid out (and this is the reason SetSizer() doesn't do it: this would be wasteful as it would be redone very soon anyhow in 99% of cases).
In your case the panel doesn't get this size event for whatever reason. Perhaps -- looking at the rest of your code -- you can see what it is and avoid it. But if not, calling Layout() is a fine solution too. Except that you should call the same method on the panel itself, not on the sizer, i.e. do
cLicensePanel::cLicensePanel( wxWindow * parent )
{
...
SetSizer(szrUserKey);
Layout();
}

Related

Revive/activate existing panels

My extension have sidebar webview that can create additional webviews as panels in the active text editor. Each of these additional webviews is for an unique item and I want to revive/activate existing webview, for the specific item, if it exists.
My issues:
I can get a list of the existing tabs with window.tabGroups.all and loop through the result. But there is no way, as far as i can see, to reactivate the desired tab. I can get some properties from there but no methods. The question here is: is there a API to get a list of the tabs and be able to revive/activate it?
Because of the first point ive decided to keep list of the instances of the additional webviews and when new webview is about the be created im checking if its unique id (in the title) is in the list and if it is then just revive the tab instead of creating a new one. Dont like this approach much but its working. The problem here is when the additional webview is closed. When closed it has to be removed from the array. Ive implemented onDidDispose for the panel but somehow the filter function, inside it, is not called:
// panels: vscode.WebviewPanel[]
// create new panel
const panel = vscode.window.createWebviewPanel(...)
// add the webview instance to the panel
const newWebview = new AdditionalWebview(panel, this.context);
this.panels.push(panel);
panel.onDidDispose(() => {
console.log("Before remove the panel"); // can see this in the console
this.panels = this.panels.filter((p) => p.title != panel.title);
console.log("Before remove the panel"); // for some reason this never appears
});
Not sure why but the panel filter functionality is never triggered (and everything after it is also not ran).
extra question: at the moment the uniqueness of the additional panels is based on their label/title. In my case thats is ok but is there any other way to get unique identifier of each tab? id/guid somewhere?
On your first question about activating a given editor, you have a couple of options.
If you know the editor's index/position in its group. That can be obtained from its tabGroup.tabs position - it seems that the tab's index in that array is faithfully its index in the editor. So you could do a Array.findIndex to get the tab uri you want to set to active.
await vscode.commands.executeCommand('workbench.action.openEditorAtIndex', [indexToOpen]);
// note the [] around the index argument
The only problem with this approach is that it works within the active group only, so you may have to activate the correct group first via:
await vscode.commands.executeCommand('workbench.action.focusSecondEditorGroup');
// or whichever group the tab you want to open is in
Or second method:
// assumes you have the tab
const openOptions = { preserveFocus: true, preview: tab.isPreview, viewColumn: tab.group.viewColumn};
// check if a uri, might be viewtype, etc., instead
if (tab.input instanceof vscode.TabInputText) {
await vscode.commands.executeCommand('vscode.open', tab.input.uri, openOptions);
}
// are your editors regular text editors?
This looks like it is opening a new tab but it will focus an existing tab if one exists at that location with that same uri.

Why does NavigationView.ContainerFromMenuItem() return null except when executed from an event handler?

I am developing a WinUI3 app containing a NavigationView. The NV uses an observable collection to populate the NavigationViewItems.
The app must support expanding and collapsing NavigationViewItems programmatically. The only way I know of to do that is to set NavigationViewItem.IsExpanded = true. And the only way I know of to get the NavigationViewItem is through NavigationView.ContainerFromMenuItem().
The trouble is, ContainerFromMenuItem always returns null except when it is executed from a button event handler (such as a Click handler). The app must perform expand/collapse without user input.
For example, I have a button that launches a Click event with this code, which works just fine to toggle an NVItem:
Category selectedItem = (Category)navview.SelectedItem;
int idx1 = Categories.IndexOf(selectedItem);
var container = (NavigationViewItem)NavView.ContainerFromMenuItem(Categories[idx1]);
if (container != null)
{
container.IsExpanded = !container.IsExpanded;
}
However, that same code, when executed during app startup, such as in the MainWindow constructor after some test items are created, or in Attach(), always results in container being null.
So what am I doing wrong?
This question is somewhat similar to UWP: NavigationView.MenuItems results empty if populated programmatically but the answer to that one only deals with in-event use of ContainerFromMenuItem().
Thanks very much for any assistance on this.

How to draw back into the scene a removed shape with Konva?

I used remove() method to delete a rectangle from the scene, how can i draw it back?
The documentation said: "remove a node from parent, but don't destroy. You can reuse the node later."
Link to documentation
I couldn't find any clue
Thanks
Thank you for the responses, i actually found a simple workaround.
I use .hide() and .show() methods because i want to keep intact the object for later use and when i do not need it anymore i'll just .destroy() the shapes.
The downside is you need more memory to keep all, but with few shapes on the scene is negligible.
Just keep a reference to the node via a variable. For example, in the code below I add a node to layer1, remove it, and add it to layer2.
var layer1 = new Konva.Layer();
stage.add(layer1);
Var node = new Konva.WhateverShape({....});
layer1.add(node);
layer1.draw();
...
...
var layer2 = new Konva.Layer();
stage.add(layer2);
node.remove(); // at this time the node exists but is not on the stage
layer2.add(node);
layer2.draw(); // now the node is visible again.

How do I listen to mouse clicks from RelatedItemLineMarkerInfo?

I need to run a custom action when my marker is clicked.
I tried, according to tutorial,
NavigationGutterIconBuilder<PsiElement> builder =
NavigationGutterIconBuilder.create(LessonScriptIcons.PUZZLE).
setTarget(this).
setTooltipText("Navigate to component");
RelatedItemLineMarkerInfo<PsiElement> m = builder.createLineMarkerInfo(this);
But I can't figure out how to get to the marker's navigation handler, or otherwise listen to this marker's events. (The cursor moving to the beginning of "this" element is a slightly undesired, but unimportant side effect that would be nice to suppress).
Then I tried this:
RelatedItemLineMarkerInfo marker = new RelatedItemLineMarkerInfo(this, getTextRange(), MyIcons.VOLUME, 0, tooltipProvider, handler, GutterIconRenderer.Alignment.CENTER, new ArrayList<GotoRelatedItem>());
This approach allows me to listen to the marker's mouse clicks. However, now I am experiencing a peculiar problem: whenever the marked line of code is edited, this results in duplicate, triplicate, etc. markers in the gutter.
What's the correct way to do it?
Turns out I needed to specify a pass.
int pass = Pass.UPDATE_OVERRIDEN_MARKERS;
RelatedItemLineMarkerInfo marker = new RelatedItemLineMarkerInfo(this, getTextRange(), MyIcons.VOLUME, pass, tooltipProvider, handler, GutterIconRenderer.Alignment.CENTER, new ArrayList<GotoRelatedItem>());
I had to use Pass.UPDATE_OVERRIDEN_MARKERS even though IntelliJ marked it as deprecated and said it will be removed shortly (see the link below). However, the constant recommended by IntelliJ, Pass.LINE_MARKERS, results in duplicate markers.
https://github.com/JetBrains/intellij-community/blob/master/platform/analysis-impl/src/com/intellij/codeHighlighting/Pass.java

Dojo dnd (drag and drop) 1.7.2 - How to maintain a separate (non-dojo-dnd) list?

I'm using Dojo dnd version 1.7.2 and it's generally working really well. I'm happy.
My app maintains many arrays of items, and as the user drags and drops items around, I need to ensure that my arrays are updated to reflect the contents the user is seeing.
In order to accomplish this, I think I need to run some code around the time of Source.onDndDrop
If I use dojo.connect to set up a handler on my Source for onDndDrop or onDrop, my code seems to get called too late. That is, the source that's passed to the handler doesn't actually have the item in it any more.
This is a problem because I want to call source.getItem(nodes[0].id) to get at the actual data that's being dragged around so I can find it in my arrays and update those arrays to reflect the change the user is making.
Perhaps I'm going about this wrong; and there's a better way?
Ok, I found a good way to do this. A hint was found in this answer to a different question:
https://stackoverflow.com/a/1635554/573110
My successful sequence of calls is basically:
var source = new dojo.dnd.Source( element, creationParams );
var dropHandler = function(source,nodes,copy){
var o = source.getItem(nodes[0].id); // 0 is cool here because singular:true.
// party on o.data ...
this.oldDrop(source,nodes,copy);
}
source.oldDrop = source.onDrop;
source.onDrop = dropHandler;
This ensures that the new implementation of onDrop (dropHandler) is called right before the previously installed one.
Kind'a shooting a blank i guess, there are a few different implementations of the dndSource. But there are a some things one needs to know about the events / checkfunctions that are called during the mouseover / dnddrop.
One approach would be to setup checkAcceptance(source, nodes) for any target you may have. Then keep a reference of the nodes currently dragged. Gets tricky though, with multiple containers that has dynamic contents.
Setup your Source, whilst overriding the checkAcceptance and use a known, (perhaps global) variable to keep track.
var lastReference = null;
var target = dojo.dnd.Source(node, {
checkAcceptance(source, nodes) : function() {
// this is called when 'nodes' are attempted dropped - on mouseover
lastReference = source.getItem(nodes[0].id)
// returning boolean here will either green-light or deny your drop
// use fallback (default) behavior like so:
return this.inhertied(arguments);
}
});
Best approach might just be like this - you get both target and source plus nodes at hand, however you need to find out which is the right stack to look for the node in. I believe it is published at same time as the event (onDrop) youre allready using:
dojo.subscribe("/dnd/drop", function(source, nodes, copy, target) {
// figure out your source container id and target dropzone id
// do stuff with nodes
var itemId = nodes[0].id
}
Available mechanics/topics through dojo.subscribe and events are listed here
http://dojotoolkit.org/reference-guide/1.7/dojo/dnd.html#manager