I have a crossplatform app that has a gtk.StatusIcon sitting in the tray, and a right click context menu. The problem is: on Windows machines the placement of the menu is awful. The top of the menu starts at the mouse pointer and so most of the menu extends below the bottom of the screen. This can then be scrolled up and is usable, but it is a bit of a pain for the user.
Another related question, is it possible to make the menu disappear if the user clicks somewhere else on the screen?
To avoid this "scrolling menu" problem on Windows you need to replace gtk.status_icon_position_menu with None in "popup-menu" signal callback.
def popup_menu_cb(status_icon, button, activate_time, menu):
menu.popup(None, None, None, button, activate_time)
The menu will show on the mouse cursor but that's how all windows programs do it.
Don't know how to hide it though... the only thing I found to work is pressing a mouse button on the menu and releasing it outside. :P
You can hide the popup when the mouse moves away by enabling the leave_notify and enter_notify events on the popup. Then use these to set and clear a time stamp. Then in a timer callback created with gobject.timeout_add() check to see if the mouse has been away from the popup menu for a certain amount of time. If it has then hide() the popup and clear the timer.
Here are the event and timer call backs I'm using:
. . .
self.mouse_in_tray_menu = None
gobject.timeout_add(500, self.check_hide_popup)
. . .
def on_tray_menu_enter_notify_event(self, widget, event, data = None):
self.mouse_in_tray_menu = None
def on_tray_menu_leave_notify_event(self, widget, event, data = None):
self.mouse_in_tray_menu = event.time + 1 # Timeout in 1 sec
def check_hide_popup(self, data = None):
if self.mouse_in_tray_menu and self.mouse_in_tray_menu < time.time():
self.tray_menu.hide()
self.mouse_in_tray_menu = None
return True # Keep the timer callback running
You don't have to keep the timer running all the time but it is easier and I am also using it for other things. The calls to enter_notify and leave_notify are somewhat erratic so the timer is necessary.
BTW, this is really only necessary in Windows because in Linux you can click elsewhere and the popup will close.
I found a solution to fix the popup menu won't hide problem on windows.
Just add following code (my code is in C but you can change it to python or whatever) before popping up the menu:
GtkWidget *hidden_window;
hidden_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_resizable (GTK_WINDOW (hidden_window), FALSE);
gtk_window_set_decorated (GTK_WINDOW (hidden_window), FALSE);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (hidden_window), TRUE);
gtk_window_set_skip_pager_hint (GTK_WINDOW (hidden_window), TRUE);
gtk_widget_set_size_request (hidden_window, 0, 0);
gtk_window_set_transient_for (GTK_WINDOW (hidden_window), GTK_WINDOW (widget)); //widget is your main window, this is to hide dummy window from taskbar
gtk_window_set_position (GTK_WINDOW (hidden_window), GTK_WIN_POS_MOUSE);
gtk_widget_set_events (hidden_window, GDK_FOCUS_CHANGE_MASK);
g_signal_connect (G_OBJECT (hidden_window),
"focus-out-event",
G_CALLBACK (on_hidden_window_focus_out),
NULL);
gtk_widget_show_all (hidden_window);
gtk_widget_grab_focus (hidden_window);
also add this function:
static void on_hidden_window_focus_out(GtkWidget *widget,
GdkEventFocus *event,
gpointer data)
{
gtk_widget_destroy (widget);
}
The idea is to create a 1x1 top level window at mouse position and grab the focus, and add destroy function when focus out.
Related
I am trying to make a Stack and StackSwitcher whose pages associated tabs can be added and deleted dynamically. To delete the associated widget in the stack and the tab in the StackSwitcher, I want to have a small delete button in the tab. I have found an example similar to this in the gtk3-widget-factory, screenshot shown below.
However, this uses a Notebook and I would like to be able to use the Stack/StackSwitcher setup instead of a Notebook (I don't like how the Notebook creates a background to its pages). I can't figure out how to change the widget that is displayed in the StackSwitcher for each page in the Stack. My intuition is that I should just be able to replace the Label in the StackSwitcher with a Box that has been packed with a Label and Button that has a callback that goes and removes the page and the tab. But I am not able to do this with how I am currently creating the pages of the Stack, which is by calling stack.add_titled(). The StackSwitcher that has been assigned to that stack just automatically picks up the labels with no way of editing them that I have found.
Is there a way to do this for a Stack and StackSwitcher? If not, I would also appreciate some advice for doing it with a Notebook as I have not even been able to recreate what I saw in the widget factory (above). This is the code that I have attempting to recreate it:
void main (string[] args) {
Gtk.init (ref args);
var window = new Window ();
window.title = "Example Program";
window.border_width = 10;
window.window_position = WindowPosition.CENTER;
window.set_default_size (350, 70);
window.destroy.connect (Gtk.main_quit);
Notebook note = new Notebook();
Box tabBox = new Box(Orientation.HORIZONTAL, 0);
Label tabLabel = new Label("Closable Tab");
Button closeButton = new Button.from_icon_name("window-close-symbolic", IconSize.BUTTON);
tabBox.pack_start(tabLabel, false, false, 0);
tabBox.pack_start(closeButton, false, false, 0);
Label displayLabel = new Label("this page should be closeable");
note.append_page(displayLabel, tabBox);
window.add(note);
window.show_all();
Gtk.main ();
}
which just creates an empty Notebook tab like this:
and I don't get any error messages about creating that tab failing so I don't know what to do. Any help is appreciated.
For adding search bar to the GTK3 app (in C). I'm using the following cooe:
pad->priv->searchbar = GTK_SEARCH_BAR (gtk_search_bar_new ());
gtk_search_bar_set_show_close_button (pad->priv->searchbar, TRUE);
GtkSearchEntry *searchentry = GTK_SEARCH_ENTRY (gtk_search_entry_new ());
gtk_container_add (GTK_CONTAINER (pad->priv->searchbar),
GTK_WIDGET (searchentry));
/* Below does nothing (I mean haligh/valigh*/
// (meant to put searchentry with no more free space in search_bar
// (to the left)
// I could also add a label Find but what for? I just want
// GtkSearchEntry to occupy the whole GtkSearchBar apart for close btn
/* Nothing of that works */
//gtk_widget_set_halign (GTK_WIDGET (searchentry), GTK_ALIGN_START);
//gtk_widget_set_valign (GTK_WIDGET (searchentry), GTK_ALIGN_START);
gtk_search_bar_connect_entry (GTK_SEARCH_BAR (pad->priv->searchbar),
GTK_ENTRY (searchentry));
// Searchbar should appear in right-top corner of the overlay
// to not cover the text which is searched (assuming that first
// several lines most probably are short enough to not be covered)
/* So these line do work but only for the GtkSearchBar itself
not got GtkSearchEntry inside of it) (/
gtk_widget_set_halign (GTK_WIDGET (pad->priv->searchbar), GTK_ALIGN_END);
gtk_widget_set_valign (GTK_WIDGET (pad->priv->searchbar), GTK_ALIGN_START);
// Jere a Just add a scroll with a text and a GtkSearchBar to a GtkOverlay
pad->priv->text_with_search_overlay = GTK_OVERLAY (gtk_overlay_new ());
gtk_overlay_add_overlay (pad->priv->text_with_search_overlay, pad->priv->scrollbar);
gtk_overlay_add_overlay (pad->priv->text_with_search_overlay, GTK_WIDGET (pad->priv->searchbar));
This works nicely. I'm just not able to customize gtk_search_bar. Do I have not yo use gtk_search_bar and create my own vbox in the overlay for this to happen? This way there will be no gtk_search_bar at all, just my widget with gtk_search_entry an button, manually removing it.
Whas it now can be observed: https://imagebin.ca/v/4Uo7Bqi6Pv7J. I don't like empty space in the left corner of the GtkSearchBar. Is it customizable or do I have to write my own search bar (like HBox with stuff). I can get rid of it by creating a Hbox,adding GtkSearchEnttry and a close button. But it will remove convenient function to show/hide it: gtk_search_bar_set_search_mode - TRUE/FALSE and I like that it exists
Is there a way to "style" the GtkSearchBar somehow. Maybe here:
gtk_search_bar_connect_entry (GTK_SEARCH_BAR (pad->priv->searchbar), GTK_ENTRY (searchentry)); ?
I'm doing trying to use halign/valign for the gtk_search_bar:
gtk_widget_set_halign (GTK_WIDGET (pad->priv->searchbar), GTK_ALIGN_END);
gtk_widget_set_valign (GTK_WIDGET (pad->priv->searchbar), GTK_ALIGN_START);
But doing so for gtk_search_entry:
gtk_widget_set_halign (GTK_WIDGET (searchentry), GTK_ALIGN_END);
gtk_widget_set_valign (GTK_WIDGET (searchentry), GTK_ALIGN_START);
does nothing
This works for GtkSearchBsr. how to use for elements inside gtk_search_bar? Is there other way then create their layout by hand?
Currently in my application I'm grabing the pointer to know when user clicks outside of some of my first window's widgets to hide my second window. Unfortunately if I grab the pointer then user needs to click outside of my application twice to set focus on another window.
When window gets focus I grab the pointer:
this.focus_in_event.connect(()=>{
var pointer = Gdk.Display.get_default ().get_device_manager ().get_client_pointer ();
pointer.grab (this.get_window (), Gdk.GrabOwnership.NONE, true,
Gdk.EventMask.BUTTON_PRESS_MASK, null, Gdk.CURRENT_TIME);
Gtk.device_grab_add (this, pointer, false);
return false;
});
When a click events occurs and some of window's widgets didn't 'lock' the pointer:
this.button_press_event.connect ( ()=>{
if (!lock_mouse_click) {
var pointer = Gdk.Display.get_default ().get_device_manager ().get_client_pointer ();
Gtk.device_grab_remove (this, pointer);
pointer.ungrab (Gdk.CURRENT_TIME);
feed_view.hide ();
}
lock_mouse_click = false;
return false;
});
What I would like to do is to "peek" at the pointer instead of stealing the mouse events outside of my application.
My code in vala but you may answer me in any language that uses gtk.
why dont you use "enter-notify-signal" ? when apointer enter your widget's window, create a on/off switch somewhere and use it in "button-press-signal". if its on when button is pressed do somethin, if its off do something else
Is there a way to add an Event.ContextClick to a Gui.Window in a Unity Editor script?
The following is my context menu method that I've tried calling from both OnGUI() and my window's WindowFunction (call sites denoted below as "site: no luck"). I have not been able to get the "Success" message to show up unless I'm right clicking directly in the main editor window. If I right click in any of the Gui.Windows I have created, the ContextClick event doesn't show up.
void OnStateContextMenu(){
Event evt = Event.current;
// Ignore anything but contextclicks
if(evt.type != EventType.ContextClick)return;
Debug.Log("Success");
// Add generic menu at context point
GenericMenu menu = new GenericMenu();
menu.AddItem (new GUIContent ("AddState"),false,AddState,evt.mousePosition);
menu.ShowAsContext ();
evt.Use();
}
And the call site(s):
void doWindow(int id){
// OnStateContextMenu(); //site1: no luck
GUI.DragWindow();
}
void OnGUI(){
OnStateContextMenu(); //site2: no luck here either
BeginWindows();
wndRect = GUI.Window(0,wndRect,doWindow,"StateWnd");
EndWindows();
}
Update
For reference, green area responds to right-click, red area does not. But I want it to. The right-click menu I've created has specific actions I only want visible if the mouse cursor right clicks inside one of my windows, the 'Hello' in the image. Note: Ignore the button, right click doesn't work anywhere inside that window.
This might not directly answer your question but should be able to help
You are trying to achieve a rightclick function inside your red box( as shown in picute )
I had a sort alike question a while back but it was not for a rightclick but for a mouseover
so i figured this might be able to help you
string mouseover; // first of i created a new string
if (GUI.Button (new Rect (100,100,200,200),new GUIContent("Load game", "MouseOverOnButton0") ,menutexture ))
{
//added a mousoveronbutton command to my GUIcontent
executestuff();
}
buttoncheck();
}
void buttoncheck()
{
mouseover = GUI.tooltip;
if(mouseover == "MouseOverOnButton0")
{
GUI.Box(new Rect(380,45,235,25),"Not a implemented function as of yet ");
}
}
this code made a new gui box the moment the mouse hitted the box.
If you created the hello in a seperate box you could use this
if(mouseover == hello)
{
if(rightclick == true)
{
execute the stuff you want
}
}
or something like that. Hope this helps a bit atleast
UPDATE
To obtain the rightclick event you will have to use the
if(Event.current.button == 1 && Event.current.isMouse)
You have to place this in the OnGUI to work properly
this way you first trigger the in box part, then check for a right click and execute the stuff you want.
I attached a listener to the Shell on SWT.MouseUp and SWT.MouseDown events, but the handleEvent method never gets fired. I tried clicking at many places on the window, but it doesn't get even to the System.out.println(..) in the code below...
Do you spot any error here?
Thank you!
//c is a Composite.
final Listener l = new Listener(){
public void handleEvent(Event event) {
System.out.println("Got event. "+event);
Rectangle rect = c.getBounds();
if (rect.contains(event.x, event.y)){
if((Boolean)c.getData("selected")){
c.setData("selected", Boolean.FALSE);
}else{
c.setData("selected", Boolean.TRUE);
}
}
}
};
c.getShell().addListener(SWT.MouseUp, l);
c.getShell().addListener(SWT.MouseDown, l);
(This composite is inside an Eclipse editor that uses the Forms Toolkit)
Regards,
-Pradyumna
By writing
c.getShell().addListener(SWT.MouseUp, l);
c.getShell().addListener(SWT.MouseDown, l);
you add the listeners to the shell only! Clicking on a child of the shell does not trigger an event for c.getShell(). Try to click near the border of the window and watch out for your trace message.
If you want to get events for the clicks on c, you have to add listeners to c via c.addListener(.). If you do that, you won't need the condition rect.contains(event.x, event.y) because you know, that the click happened on c.