annotate src/libs/garden/garden.contextmenu/context_menu.py @ 83:741a7d6d8c28

garden: added contextmenu
author Goffi <goffi@goffi.org>
date Sat, 24 Dec 2016 14:16:58 +0100
parents
children 2caee196d19a
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
83
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
1 from kivy.uix.gridlayout import GridLayout
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
2 from kivy.uix.relativelayout import RelativeLayout
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
3 from kivy.core.window import Window
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
4 from kivy.uix.behaviors import ButtonBehavior
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
5 from kivy.lang import Builder
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
6 from kivy.clock import Clock
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
7 from functools import partial
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
8 import traceback
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
9
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
10 import kivy.properties as kp
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
11 import os
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
12
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
13
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
14 class AbstractMenu(object):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
15 cancel_handler_widget = kp.ObjectProperty(None)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
16 bounding_box_widget = kp.ObjectProperty(None)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
17
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
18 def __init__(self, *args, **kwargs):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
19 self.clock_event = None
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
20
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
21 def add_item(self, widget):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
22 self.add_widget(widget)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
23
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
24 def add_text_item(self, text, on_release=None):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
25 item = ContextMenuTextItem(text=text)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
26 if on_release:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
27 item.bind(on_release=on_release)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
28 self.add_item(item)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
29
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
30 def get_height(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
31 height = 0
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
32 for widget in self.children:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
33 height += widget.height
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
34 return height
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
35
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
36 def hide_submenus(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
37 for widget in self.menu_item_widgets:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
38 widget.hovered = False
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
39 widget.hide_submenu()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
40
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
41 def self_or_submenu_collide_with_point(self, x, y):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
42 raise NotImplementedError()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
43
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
44 def on_cancel_handler_widget(self, obj, widget):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
45 self.cancel_handler_widget.bind(on_touch_down=self.hide_app_menus)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
46
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
47 def hide_app_menus(self, obj, pos):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
48 raise NotImplementedError()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
49
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
50 @property
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
51 def menu_item_widgets(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
52 """
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
53 Return all children that are subclasses of ContextMenuItem
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
54 """
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
55 return [w for w in self.children if issubclass(w.__class__, AbstractMenuItem)]
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
56
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
57 def _setup_hover_timer(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
58 if self.clock_event is None:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
59 self.clock_event = Clock.schedule_interval(partial(self._check_mouse_hover), 0.05)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
60
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
61 def _check_mouse_hover(self, obj):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
62 self.self_or_submenu_collide_with_point(*Window.mouse_pos)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
63
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
64 def _cancel_hover_timer(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
65 if self.clock_event:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
66 self.clock_event.cancel()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
67 self.clock_event = None
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
68
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
69
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
70 class ContextMenu(GridLayout, AbstractMenu):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
71 visible = kp.BooleanProperty(False)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
72 spacer = kp.ObjectProperty(None)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
73
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
74 def __init__(self, *args, **kwargs):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
75 super(ContextMenu, self).__init__(*args, **kwargs)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
76 self.orig_parent = None
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
77 # self._on_visible(False)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
78
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
79 def hide(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
80 self.visible = False
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
81
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
82 def show(self, x=None, y=None):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
83 self.visible = True
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
84 self._add_to_parent()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
85 self.hide_submenus()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
86
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
87 root_parent = self.bounding_box_widget if self.bounding_box_widget is not None else self.get_context_menu_root_parent()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
88 if root_parent is None:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
89 return
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
90
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
91 point_relative_to_root = root_parent.to_local(*self.to_window(x, y))
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
92
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
93 # Choose the best position to open the menu
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
94 if x is not None and y is not None:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
95 if point_relative_to_root[0] + self.width < root_parent.width:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
96 pox_x = x
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
97 else:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
98 pox_x = x - self.width
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
99 if issubclass(self.parent.__class__, AbstractMenuItem):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
100 pox_x -= self.parent.width
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
101
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
102 if point_relative_to_root[1] - self.height < 0:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
103 pos_y = y
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
104 if issubclass(self.parent.__class__, AbstractMenuItem):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
105 pos_y -= self.parent.height + self.spacer.height
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
106 else:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
107 pos_y = y - self.height
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
108
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
109 parent_pos = root_parent.pos
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
110 pos = (pox_x + parent_pos[0], pos_y + parent_pos[1])
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
111
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
112 self.pos = pos
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
113
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
114 def self_or_submenu_collide_with_point(self, x, y):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
115 queue = self.menu_item_widgets
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
116 collide_widget = None
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
117
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
118 # Iterate all siblings and all children
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
119 while len(queue) > 0:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
120 widget = queue.pop(0)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
121 submenu = widget.get_submenu()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
122 if submenu is not None and widget.hovered:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
123 queue += submenu.menu_item_widgets
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
124
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
125 widget_pos = widget.to_window(0, 0)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
126 if widget.collide_point(x - widget_pos[0], y - widget_pos[1]) and not widget.disabled:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
127 widget.hovered = True
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
128
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
129 collide_widget = widget
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
130 for sib in widget.siblings:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
131 sib.hovered = False
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
132 elif submenu and submenu.visible:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
133 widget.hovered = True
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
134 else:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
135 widget.hovered = False
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
136
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
137 return collide_widget
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
138
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
139 def _on_visible(self, new_visibility):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
140 if new_visibility:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
141 self.size = self.get_max_width(), self.get_height()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
142 self._add_to_parent()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
143 # @todo: Do we need to remove self from self.parent.__context_menus? Probably not.
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
144
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
145 elif self.parent and not new_visibility:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
146 self.orig_parent = self.parent
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
147
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
148 '''
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
149 We create a set that holds references to all context menus in the parent widget.
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
150 It's necessary to keep at least one reference to this context menu. Otherwise when
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
151 removed from parent it might get de-allocated by GC.
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
152 '''
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
153 if not hasattr(self.parent, '_ContextMenu__context_menus'):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
154 self.parent.__context_menus = set()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
155 self.parent.__context_menus.add(self)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
156
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
157 self.parent.remove_widget(self)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
158 self.hide_submenus()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
159 self._cancel_hover_timer()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
160
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
161 def _add_to_parent(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
162 if not self.parent:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
163 self.orig_parent.add_widget(self)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
164 self.orig_parent = None
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
165
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
166 # Create the timer on the outer most menu object
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
167 if self._get_root_context_menu() == self:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
168 self._setup_hover_timer()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
169
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
170 def get_max_width(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
171 max_width = 0
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
172 for widget in self.menu_item_widgets:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
173 width = widget.content_width if widget.content_width is not None else widget.width
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
174 if width is not None and width > max_width:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
175 max_width = width
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
176
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
177 return max_width
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
178
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
179 def get_context_menu_root_parent(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
180 """
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
181 Return the bounding box widget for positioning sub menus. By default it's root context menu's parent.
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
182 """
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
183 if self.bounding_box_widget is not None:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
184 return self.bounding_box_widget
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
185 root_context_menu = self._get_root_context_menu()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
186 return root_context_menu.bounding_box_widget if root_context_menu.bounding_box_widget else root_context_menu.parent
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
187
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
188 def _get_root_context_menu(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
189 """
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
190 Return the outer most context menu object
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
191 """
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
192 root = self
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
193 while issubclass(root.parent.__class__, ContextMenuItem) \
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
194 or issubclass(root.parent.__class__, ContextMenu):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
195 root = root.parent
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
196 return root
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
197
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
198 def hide_app_menus(self, obj, pos):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
199 return self.self_or_submenu_collide_with_point(pos.x, pos.y) is None and self.hide()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
200
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
201
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
202 class AbstractMenuItem(object):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
203 submenu = kp.ObjectProperty(None)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
204
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
205 def get_submenu(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
206 return self.submenu if self.submenu != "" else None
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
207
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
208 def show_submenu(self, x=None, y=None):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
209 if self.get_submenu():
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
210 self.get_submenu().show(*self._root_parent.to_local(x, y))
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
211
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
212 def hide_submenu(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
213 submenu = self.get_submenu()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
214 if submenu:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
215 submenu.visible = False
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
216 submenu.hide_submenus()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
217
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
218 def _check_submenu(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
219 if self.parent is not None and len(self.children) > 0:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
220 submenus = [w for w in self.children if issubclass(w.__class__, ContextMenu)]
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
221 if len(submenus) > 1:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
222 raise Exception('Menu item (ContextMenuItem) can have maximum one submenu (ContextMenu)')
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
223 elif len(submenus) == 1:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
224 self.submenu = submenus[0]
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
225
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
226 @property
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
227 def siblings(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
228 return [w for w in self.parent.children if issubclass(w.__class__, AbstractMenuItem) and w != self]
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
229
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
230 @property
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
231 def content_width(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
232 return None
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
233
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
234 @property
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
235 def _root_parent(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
236 return self.parent.get_context_menu_root_parent()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
237
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
238
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
239 class ContextMenuItem(RelativeLayout, AbstractMenuItem):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
240 submenu_arrow = kp.ObjectProperty(None)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
241
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
242 def _check_submenu(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
243 super(ContextMenuItem, self)._check_submenu()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
244 if self.get_submenu() is None:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
245 self.submenu_arrow.opacity = 0
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
246 else:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
247 self.submenu_arrow.opacity = 1
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
248
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
249
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
250 class AbstractMenuItemHoverable(object):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
251 hovered = kp.BooleanProperty(False)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
252
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
253 def _on_hovered(self, new_hovered):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
254 if new_hovered:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
255 spacer_height = self.parent.spacer.height if self.parent.spacer else 0
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
256 point = self.right, self.top + spacer_height
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
257 self.show_submenu(self.width, self.height + spacer_height)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
258 else:
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
259 self.hide_submenu()
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
260
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
261
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
262 class ContextMenuText(ContextMenuItem):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
263 label = kp.ObjectProperty(None)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
264 submenu_postfix = kp.StringProperty(' ...')
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
265 text = kp.StringProperty('')
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
266 font_size = kp.NumericProperty(14)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
267 color = kp.ListProperty([1,1,1,1])
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
268
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
269 def __init__(self, *args, **kwargs):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
270 super(ContextMenuText, self).__init__(*args, **kwargs)
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
271
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
272 @property
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
273 def content_width(self):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
274 # keep little space for eventual arrow for submenus
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
275 return self.label.texture_size[0] + 10
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
276
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
277
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
278 class ContextMenuDivider(ContextMenuText):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
279 pass
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
280
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
281
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
282 class ContextMenuTextItem(ButtonBehavior, ContextMenuText, AbstractMenuItemHoverable):
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
283 pass
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
284
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
285
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
286 _path = os.path.dirname(os.path.realpath(__file__))
741a7d6d8c28 garden: added contextmenu
Goffi <goffi@goffi.org>
parents:
diff changeset
287 Builder.load_file(os.path.join(_path, 'context_menu.kv'))