bumblebee-status/bumblebee_status/util/popup.py
tobi-wan-kenobi 08b5386140 [util/popup] fix endless loop on "close on leave"
When closing a popup window when the mouse leave the area (default
behaviour, unfortunately), the main "show()" got stuck in an infinite
loop.

Fix that by setting running to False when exiting.

fixes 
2022-01-14 13:39:04 +01:00

114 lines
2.9 KiB
Python

"""Pop-up menus."""
import logging
import tkinter as tk
import functools
class menu(object):
"""Draws a hierarchical popup menu
:param parent: If given, this menu is a leave of the "parent" menu
:param leave: If set to True, close this menu when mouse leaves the area (defaults to True)
"""
def __init__(self, parent=None, leave=True):
self.running = True
self.parent = parent
self._root = parent.root() if parent else tk.Tk()
self._root.withdraw()
self._menu = tk.Menu(self._root, tearoff=0)
self._menu.bind("<FocusOut>", self.__on_focus_out)
if leave:
self._menu.bind("<Leave>", self.__on_focus_out)
elif not parent:
self.add_menuitem("close", self.__on_focus_out)
self.add_separator()
self._menu.bind("<ButtonRelease-1>", self.release)
"""Returns the root node of this menu
:return: root node
"""
def root(self):
return self._root
"""Returns the menu
:return: menu
"""
def menu(self):
return self._menu
def __on_focus_out(self, event=None):
self.running = False
self._root.destroy()
def __on_click(self, callback):
self._root.destroy()
callback()
def release(self, event=None):
self.running=False
if self.parent:
self.parent.release(event)
"""Adds a cascading submenu to the current menu
:param menuitem: label to display for the submenu
:param submenu: submenu to show
"""
def add_cascade(self, menuitem, submenu):
self._menu.add_cascade(label=menuitem, menu=submenu.menu())
"""Adds an item to the current menu
:param menuitem: label to display for the entry
:param callback: method to invoke on click
"""
def add_menuitem(self, menuitem, callback):
self._menu.add_command(
label=menuitem, command=functools.partial(self.__on_click, callback)
)
"""Adds a separator to the menu in the current location"""
def add_separator(self):
self._menu.add_separator()
"""Shows this menu
:param event: i3wm event that triggered the menu (dict that contains "x" and "y" fields)
:param offset_x: x-axis offset from mouse position for the menu (defaults to 0)
:param offset_y: y-axis offset from mouse position for the menu (defaults to 0)
"""
def show(self, event, offset_x=0, offset_y=0):
try:
self._menu.tk_popup(event["x"] + offset_x, event["y"] + offset_y)
finally:
self._menu.grab_release()
while self.running == True:
try:
self._root.update_idletasks()
self._root.update()
except:
self.running = False
try:
self._root.destroy()
except:
pass
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4