My application consists of a tree view with files that the user can click on. Based on the file the preview area will show the appropriate control to display that file. For some files that is a GtkGlArea that is rendering the content. It includes a camera that should be movable using the keyboard and mouse.
When a file is clicked the following happens (inside the event handler of the changed event of the GtkTreeView)
if (mActiveView->needsOpenGL()) {
gtk_widget_show(mGlControl);
gtk_widget_grab_focus(mGlControl);
} else {
gtk_widget_hide(mGlControl);
}
The mGlControl is created using a GtkBuilder that reads my Glade UI template. The control has the flags Visible, Can focus, Application paintable, Double Buffered, Sensitive and the events Pointer Motion, Button Press, Button Release, Key Press, Key Release, Focus Change, Structure
However when an element is clicked in the tree view the focus stays on that item and when I use my keyboard it is reflected in the tree view (using arrow up selects the next item for example).
Is it possible to put the focus on a GtkGlArea?
It turns out the intuition I had after re-reading my posted question was correct. It seems switching focus inside the event handler of the tree view change event was not working. I think the chain of events is probably like this:
Item is clicked
Changed event is handled and delegated -> focus set on the gl control
"Click" event on the item is handled -> focus set on the tree view item
It seems the focus logic for clicking an item is done after the change event is issued.
I adapted my code to look like this:
if (mActiveView->needsOpenGL()) {
gtk_widget_show(mGlControl);
runInMainThread([this]() { gtk_widget_grab_focus(mGlControl); });
} else {
gtk_widget_hide(mGlControl);
}
This is a convenience method:
void MainWindow::runInMainThread(const std::function<void()> &callback) {
g_idle_add(callbacks::mainThreadCallback, new callbacks::FunctionHolder{callback});
}
And should delay it for after the processing of the click event and now the tree view item gets properly defocused.
Related
In my mfc dialog based application, there is a CListCtrl. I need to disable a button when the user clicks on an empty item in the list control.I used NM_CLICK message and achieved this. But if the user drags the mouse out of list control area and releases the mouse this doesn't work. I found the reason that NM_CLICK will be called only on receiving button up message.Is there any other solution for this.
But if the user drags the mouse out of list control area and releases the mouse this doesn't work.
That is entirely by design, and you shouldn't want those clicks to "count". This is the only way that a user has of changing her mind in the middle of a click. It works like this:
Start to click on an object (or the empty space) in the list box control
Change your mind
While still holding down the mouse button (i.e., before committing your click), drag the mouse pointer outside of the bounds of the control
Think: Whew! That was a close call!
You'll notice that, in Windows, an action never happens until the mouse button is released (often called "MouseUp"). If this wasn't supported, there would be no way for the user to bail out early of an action, which is a critical feature of any user interface.
There are some feasible options.
In parent dialog, MouseUp Handler function can use for this.
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
By using point, you can check whether mouse releasing is occured CListCtrl.
If it is, you just disable the button.
Also, WindowFromPoint can be another option.
static CWnd* PASCAL WindowFromPoint(POINT point);
In such as a OnMouseMove, if you use this function, you can check the window is pointed by mouse.
Implement a LVN_ITEMCHANGED handler, and in that handler disable the button as soon as the selected item count is zero.
I found the solution for the issue. There is a message which can be handled if the user drags the mouse using left button namely LVN_BEGINDRAG.By handling this message in the OnBeginDrag function I update the button status.So the button gets updated as soon as the user tries to drag the mouse.The code is as below:
In the header add
afx_msg void OnBeginDrag( NMHDR* pNMHDR, LRESULT *pResult );
In the implementation add message map and corresponding function:
BEGIN_MESSAGE_MAP( .. )
ON_NOTIFY( LVN_BEGINDRAG, IDC_LIST1, OnBeginDrag )
END_MESSAGE_MAP
OnBeginDrag( .... )
{
Updatebutton();
}
It solves the issue. thank you all for the support.
I have a app that has items that can contain child items and so on.
Each item has a mouseDown event that i want to be able to use to click and select an item.
The problem is that when i click a child item the parents mouseDown event is also being called.
How do i stop this so the mouseDown event only gets called once or if thats not posible i actually want the function called within the mouseDown event to only get called once.
Here is a jsfiddle to very simply show this. http://jsfiddle.net/rmossuk/W9vmW/1/
If you click no the Click here its mouse down event gets called twice.
You have to return false; in the mouseDown function.
Or you can you use event.stopPropagation(). I updated your fiddle, see http://jsfiddle.net/W9vmW/2/.
There is more informations about how event bubbling in ember works here - http://emberjs.com/guides/view_layer/#toc_event-bubbling
Events will bubble up the view hierarchy until the event reaches the root view. An event handler can stop propagation using the same techniques as normal jQuery event handlers:
return false from the method
event.stopPropagation
If I click anywhere on the dialog window, like in the "background", or on a Static Text, the function OnLButtonDown() is fired. But if I click a ListBox/EditBox/Combo/Calendar/Checkbox etc is not fired.
I thought that because these have control variables atached to it, and the Static Text don't. But adding a new Listbox to my Dialog and testing, I see that it doesn't fire either, so now I am confused...
I added OnLButtonDown() with the Class Wizard, it appears in:
BEGIN_MESSAGE_MAP(CMFCTesting2Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
// other handlers, etc
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
My function:
void CMFCTesting2Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
AfxMessageBox(CString("BUTTON DOWN"));
CDialogEx::OnLButtonDown(nFlags, point);
}
I tried calling CDialogEx:: ... before AfxMessageBox... but same result.
The CListBox item, has the Notify option set to True, as I seen some advices in other posts.
I suspect that the WM notification message is captured somehow by the ListBox, and not sent "forward" to my CMFCTesting2Dlg, but I understood that this should happen with the Notify option set to True, on the ListBox, no? (well, obviously, not...)
Sorry, I am new to dealing with WM in MFC.
BTW, I use Visual Studio 2010 Ultimate, and this is a Visual C++ - MFC- MFC Application - Dialog Based project.
How can I capture this mouse down event if clicked on a listbox / combo / etc?
On the LONG STORY, I am actually trying to accomplish this issue:
I have two listboxes (lets say), and I want to scroll them synchronously, when user scrolls one of them, others must scroll the same (or update in the next moment). And I thought of using on mouse down to track the position of every Listbox (with the GetTopIndex), and on mouse up, to GetTopIndex again and compare with the previous ones. If a change was made, then a listbox was scrolled and then update all listboxes with SetTopIndex. The unfriendly solution for the user, but simpler for me, would be clicking a button that does this verification / update, but its not elegant at all, and it can only set them at the same level, but can't determine which one was scrolled last time. This automatically scrolling of the listboxes should be made just for displaying, it is not important the selections in the listboxes. I tried using the Message Type in the Add Event Handler for the Listbox, but none that are displayed work for my problem, KillFocus and SetFocus, are not fired if the scroll-bar is dragged, only if an item in the listbox is clicked... and I didn't succeed on the others message type either, from the Add Event Handler.
Once a window handles a message, it stops being sent to the other windows beneath it. A static window doesn't handle a mouse down, so it goes to the dialog. The other controls all handle it in some fashion (setting focus at least) so it never gets to the dialog.
You can override the OnLButtonDown handler in each of the controls to do something else once they've finished their default handling in the base class. You might also get to see the message in the PreTranslateMessage method of the dialog.
As far as getting the List Controls to sync scrolling goes, your best bet would be to intercept WM_VSCROLL by subclassing CListCtrl or perhaps by PreTranslateMessage, and then calling SetScrollInfo on the other list. If the number of items in the lists are the same, it should be fairly simple.
Below is what I came up with. Wonder if there's easier way to do it.
Suppose I want only menu layer to be touchable while it's up.
I put invisible layer that will swallow touches.
bool tNoTouchLayer::init()
{
if(!CCLayer::init()) {
return false;
}
setIsTouchEnabled(true);
return true;
}
void tNoTouchLayer::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
}
bool tNoTouchLayer::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
return true;
}
Now I can add the noTouchLayer before adding menu layer, and all touches would be stolen by noTouchLayer.
Lastly, i did find more info on this:
http://code.google.com/p/cocos2d-iphone/issues/detail?id=1033
the reason that menu items are stealing touches is because menu items have their touch priority set to the highest (lowest char value) possible...
you can change kCCMenutouchPriority to be 0 instead.
That's how you do it as far as I can tell. Note however that your code will not disable any menues added to the scene. To do that you have to remove the menu from the touch dispatcher when adding the popup and add it back again when removing the popup.
To remove a menu from the touch dispatcher you can do the following:
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(menu);
and to add it back you can do this:
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(menu, kCCMenuHandlerPriority, true);
where menu is your CCMenu node.
As a tip, I created a class like the one above but I also added the popup menu to it, creating one touch blocking menu in one node. :)
this isn't exactly the answer you are looking for, but here's something up for thought:
if you're trying to do this for a pop-up, would it be possible to try to pop up a subclass of UIAlertView (one that looks the way you want it to)?
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-uialertview-custom-graphics/
Here's another approach:
keep state of the app and which layer is "on top".
in each of your menu listeners, have them all do a check to see if the state of your current layer should allow for that menu button to be pressed.
also, you can override "addchild" to see if it's a MenuItem, and if it is a MenuItem, then have it check to see if it should be enabled. if not, return immediately instead of executing the rest of the code
If I understand your question correctly, I guess you try to do something like "pause screen" to pop up and disable all other layers.
Well, you said in your comment that you won't like to enable touch event in other areas but not your pop-up's area. Basically, I would think we should think in term of layer for easier understanding, and easier to implement.
Let's see if we have "main layer" which holds other game objects to show as its childs (assume that they also are running animation). Now you touch a button and want to pop up "pause layer". You have to do the following in order to disable all touch event from others layer + objects.
Pause layers' schedule and actions [via pauseSchedulerAndActions()]
Pause all of its game objects inside the layer (ie. enemies) [via pauseSchedulerAndActions()]
Disable CCMenu object (if any), this will ignore touch event on CCMenu related object ie.CCMenuItemImage [via setEnabled(false)]
Disable touch event for layer itself [via setTouchEnabled(false)]
The first 2 points are about stop running any schedule method, and animation.
The latter 2 points are about stop accepting touch event. You can see that CCMenu* related class maintains its own touch event separately from CCLayer, thus we need to do additional effort by set to both CCMenu* object and the layer itself.
I tried this and it works well for me. Also it's better as we don't have to involve setting dispatcher directly in my opinion.
I'm trying to have a list of Gtk::Widgets that when clicked on I need to highlight the widget, but when the user holds the button down, and drags to another widget, the first one will 'unhighlight' and the new one will highlight on enter_notify.
I've tried using the on_enter_notify_event, and on_button_press_event in multiple combinations ( yes, I'm adding the events ), but every time I 'press' the widget, it starts a drag event, and enter_notify and leave_notify won't fire.
Is there a way to ignore the drag event? I've tried ending it when it starts, but I think the problem is the on_notify won't trigger with the mouse down. Is there another way of doing it?
Any help or suggestions would be greatly appreciated.
Okay, apparently when a widget gets activated or clicked, the GdkDevice 'grab()'s the widget and only sends events to THAT widget. So I've taken the device from the event, and called 'ungrab()' on it so it allows the other cells to receive events. Then on my other cells I check the event->state in the on_enter_notify_event to see if the mouse is down, and voila! The cell is highlighted.
bool cell::on_button_press_event(GdkEventButton* event) {
highlight_cell();
gdk_device_ungrab(event->device, event->time);
return true;
}