1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
"""
Part of the tagit module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
# standard imports
import logging
import os
# kivy imports
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
import kivy.properties as kp
# tagit imports
from tagit import config
from tagit.utils.builder import InvalidFactoryName
from tagit.actions import ActionBuilder
from tagit.external.kivy_garden.contextmenu import ContextMenuItem, AbstractMenuItemHoverable, ContextMenuTextItem, ContextMenu
# inner-module imports
from .dock import DockBase
# exports
__all__ = ('Context', )
## code ##
logger = logging.getLogger(__name__)
# load kv
Builder.load_file(os.path.join(os.path.dirname(__file__), 'context.kv'))
# classes
class ContextMenuAction(ContextMenuItem, AbstractMenuItemHoverable):
"""Wraps a context menu item around an action buttons."""
# menu requirements
submenu_postfix = kp.StringProperty(' ...')
color = kp.ListProperty([1,1,1,1])
# action requirements
action = kp.ObjectProperty(None)
hide_fu = kp.ObjectProperty(None)
@property
def content_width(self):
"""Forward the width from the action button."""
if self.action is None:
return 0
return self.action.width
def set_action(self, action):
"""Add the action button."""
self.add_widget(action)
self.action = action
return self
def on_touch_up(self, touch):
"""Close the menu when an action is triggered."""
if self.collide_point(*touch.pos) and \
touch.button == 'left' and \
self.hide_fu is not None:
self.action.on_release()
self.hide_fu()
return super(ContextMenuAction, self).on_touch_up(touch)
class Context(FloatLayout, DockBase):
"""Context menu."""
root = kp.ObjectProperty(None)
def show(self, x, y):
"""Open the menu."""
self.menu.show(x, y)
def on_touch_down(self, touch):
"""Open the menu via click."""
if touch.button == 'right':
self.show(*touch.pos)
return super(Context, self).on_touch_down(touch)
def on_config_changed(self, session, key, value):
if key == ('ui', 'standalone', 'context'):
self.on_cfg(session, session.cfg)
def on_cfg(self, wx, cfg):
"""Construct the menu from config."""
self.populate(cfg('ui', 'standalone', 'context'))
def populate(self, actions):
"""Construct the menu."""
# clear old menu items
childs = [child for child in self.menu.children if isinstance(child, ContextMenuTextItem)]
childs += [child for child in self.menu.children if isinstance(child, ContextMenuAction)]
for child in childs:
self.menu.remove_widget(child)
# add new menu items
builder = ActionBuilder()
for menu, args in actions.items():
if menu == 'root':
# add directly to the context menu
wx = self.menu
else:
# create and add a submenu
head = ContextMenuTextItem(text=menu)
self.menu.add_widget(head)
wx = ContextMenu(width=self.button_width)
head.add_widget(wx)
wx._on_visible(False)
for action in args:
try:
cls = builder.get(action)
if action == 'SortKey':
# special case: add as submenu
btn = cls(root=self.root)
head = ContextMenuTextItem(text=btn.text)
wx.add_widget(head)
head.add_widget(btn.menu)
btn.menu._on_visible(False)
else:
wx.add_widget(ContextMenuAction(
# args to the action wrapper
hide_fu=self.menu.hide,
height=self.button_height,
).set_action(cls(
# args to the button
root=self.root,
autowidth=False,
size=(self.button_width, self.button_height),
size_hint=(1, None),
show=self.button_show,
)))
except InvalidFactoryName:
logger.error(f'invalid button name: {action}')
## config ##
config.declare(('ui', 'standalone', 'context'),
config.Dict(config.String(), config.List(config.Enum(set(ActionBuilder.keys())))), {},
__name__, 'Context menu structure', 'The context menu consists of groups of actions, similar to the button dock. Each group consists of a name and a list of actions. To add actions to the menu directly, use "root" for the group name.', '{"root": ["ShowDashboard", "ShowBrowsing"], "search": ["GoBack", "GoForth"]}')
## EOF ##
|