[MouseFox logo]
The MouseFox Project
Join The Community Discord Server
[Discord logo]
Edit on GitHub

kvex.widgets.theme

Home of ThemeManager and ThemeSelector.

  1"""Home of `ThemeManager` and `ThemeSelector`."""
  2
  3from .. import kivy as kv
  4from ..colors import THEME_NAMES
  5from .label import XLabel
  6from .layouts import XBox, XDBox, XAnchor, XFrame
  7from .scroll import XScroll
  8from .spinner import XSpinner, XSpinnerOption
  9
 10
 11class XThemeSelector(XSpinner):
 12    """A button for selecting the app's theme."""
 13
 14    def __init__(self, **kwargs):
 15        """Initialize the class."""
 16        kwargs = dict(option_cls=self._spinner_factory) | kwargs
 17        super().__init__(
 18            text=self.app.theme_name,
 19            values=THEME_NAMES,
 20            text_autoupdate=True,
 21            **kwargs,
 22        )
 23        self.set_size(y="50dp")
 24        self.bind(text=self._on_select)
 25        self.app.bind(on_theme=self._on_app_theme)
 26
 27    def _on_app_theme(self, *args):
 28        self.text = self.app.theme_name
 29
 30    def _on_select(self, *args):
 31        self.app.set_theme(self.text)
 32
 33    def _spinner_factory(self, *args, **kwargs):
 34        return XSpinnerOption(*args, subtheme_name="secondary", **kwargs)
 35
 36
 37class XThemePreview(kv.FocusBehavior, XBox):
 38    """Widget to preview the current theme."""
 39
 40    def __init__(self):
 41        """Initialize the class."""
 42        self._palette_box = XBox()
 43        self._title_label = XLabel(
 44            font_size="24sp",
 45            color=(0, 0, 0),
 46            outline_color=(1, 1, 1),
 47            outline_width=2,
 48            valign="top",
 49            enable_theming=False,
 50        )
 51        super().__init__(orientation="vertical")
 52        self._make_widgets()
 53        self.app.bind(on_theme=self._refresh_palette_box)
 54
 55    def keyboard_on_key_down(self, w, keycode, text, modifiers):
 56        """Switch theme using arrow keys or [shift] tab."""
 57        code, key = keycode
 58        shifted = "shift" in modifiers
 59        tabbed = key == "tab"
 60        cindex = THEME_NAMES.index(self.app.theme_name)
 61        if key == "right" or (tabbed and not shifted):
 62            cindex += 1
 63        elif key == "left" or (tabbed and shifted):
 64            cindex -= 1
 65        elif key.isdigit():
 66            cindex = int(key) - 1
 67        else:
 68            return True
 69        cindex = cindex % len(THEME_NAMES)
 70        self.app.set_theme(THEME_NAMES[cindex])
 71        return True
 72
 73    def _make_widgets(self):
 74        # Palette
 75        self._palette_frame = XAnchor()
 76        self._palette_frame.add_widgets(self._palette_box, self._title_label)
 77        self._palette_frame.set_size(y="50sp")
 78        self._refresh_palette_box()
 79        # Subthemes
 80        primary = XSubThemePreview(subtheme_name="primary")
 81        primary.set_size(hy=1.5)
 82        secondary = XSubThemePreview(subtheme_name="secondary")
 83        secondary.set_size(hx=3)
 84        accent = XSubThemePreview(subtheme_name="accent")
 85        right_frame = XBox()
 86        right_frame.add_widgets(secondary, accent)
 87        outer_frame = XBox(orientation="vertical")
 88        outer_frame.add_widgets(primary, right_frame)
 89        self.add_widgets(
 90            self._palette_frame,
 91            outer_frame,
 92        )
 93
 94    def _refresh_palette_box(self, *args):
 95        self._title_label.text = self.app.theme_name.capitalize()
 96        self._palette_box.clear_widgets()
 97        for c in self.app.theme.palette:
 98            pb = XLabel(
 99                text=c.hex.upper(),
100                outline_color=(0, 0, 0),
101                outline_width=2,
102                valign="bottom",
103                enable_theming=False,
104            )
105            pb.make_bg(c)
106            self._palette_box.add_widget(pb)
107
108
109class XSubThemePreview(XFrame):
110    """Widget to preview a SubTheme."""
111
112    def __init__(self, *args, **kwargs):
113        """Initialize the class."""
114        super().__init__(*args, **kwargs)
115        with self.app.subtheme_context(self.subtheme_name):
116            self._make_widgets()
117
118    def _make_widgets(self):
119        self._title_label = XLabel(font_size="24sp", shorten=True, shorten_from="right")
120        self._title_label.set_size(y="32sp")
121        self._br = XAnchor()
122        self._br.set_size(y="8dp")
123        self._detail_label = XLabel(
124            fixed_width=True,
125            padding=(10, 10),
126            halign="left",
127            valign="top",
128        )
129        self._br2 = XAnchor()
130        self._br2.set_size(hx=0.5, y="4dp")
131        br2_wrapped = XAnchor.wrap(self._br2)
132        br2_wrapped.set_size(y="10dp")
133        self._lorem_label = XLabel(
134            text=LOREM_IPSUM,
135            fixed_width=True,
136            padding=(10, 10),
137            halign="left",
138            valign="top",
139        )
140        scroll_view = XDBox()
141        scroll_view.add_widgets(
142            self._detail_label,
143            br2_wrapped,
144            self._lorem_label,
145        )
146        self._label_scroll = XScroll(view=scroll_view)
147        main_frame = XBox(orientation="vertical", padding="10sp")
148        main_frame.add_widgets(
149            self._title_label,
150            self._br,
151            self._label_scroll,
152        )
153        self.add_widget(main_frame)
154
155    def on_subtheme(self, subtheme):
156        """Apply colors."""
157        super().on_subtheme(subtheme)
158        self._br.make_bg(subtheme.accent1)
159        self._br2.make_bg(subtheme.accent2)
160        self._title_label.text = self.subtheme_name.capitalize()
161        self._detail_label.text = self._get_detail_text(subtheme)
162
163    def _get_detail_text(self, subtheme):
164        bullet = subtheme.accent2.markup("•")
165        fg2m = subtheme.fg2.markup
166        hexes = "\n".join((
167            f"{bullet} {color_name} color {fg2m(color.hex.upper())}"
168            for color_name, color in subtheme._asdict().items()
169        ))
170        name = self.app.theme_name.capitalize()
171        return f"[size=20sp][u]{name} theme[/u][/size]\n{hexes}"
172
173
174LOREM_IPSUM = (
175    "Non eram nescius, Brute, cum, quae summis ingeniis exquisitaque doctrina"
176    " philosophi Graeco sermone tractavissent, ea Latinis litteris mandaremus, fore"
177    " ut hic noster labor in varias reprehensiones incurreret.\n\nNam quibusdam, et iis"
178    " quidem non admodum indoctis, totum hoc displicet philosophari. quidam autem non"
179    " tam id reprehendunt, si remissius agatur, sed tantum studium tamque multam"
180    " operam ponendam in eo non arbitrantur. erunt etiam, et ii quidem eruditi"
181    " Graecis litteris, contemnentes Latinas, qui se dicant in Graecis legendis"
182    " operam malle consumere. postremo aliquos futuros suspicor, qui me ad alias"
183    " litteras vocent, genus hoc scribendi, etsi sit elegans, personae tamen et"
184    " dignitatis esse negent.\n\nContra quos omnis dicendum breviter existimo. Quamquam"
185    " philosophiae quidem vituperatoribus satis responsum est eo libro, quo a nobis"
186    " philosophia defensa et collaudata est, cum esset accusata et vituperata ab"
187    " Hortensio. qui liber cum et tibi probatus videretur et iis, quos ego posse"
188    " iudicare arbitrarer, plura suscepi veritus ne movere hominum studia viderer,"
189    " retinere non posse.\n\nQui autem, si maxime hoc placeat, moderatius tamen id"
190    " volunt fieri, difficilem quandam temperantiam postulant in eo, quod semel"
191    " admissum coerceri reprimique non potest, ut propemodum iustioribus utamur"
192    " illis, qui omnino avocent a philosophia, quam his, qui rebus infinitis modum"
193    " constituant in reque eo meliore, quo maior sit, mediocritatem desiderent.\n\nSive"
194    " enim ad sapientiam perveniri potest, non paranda nobis solum ea, sed fruenda"
195    " etiam [sapientia] est; sive hoc difficile est, tamen nec modus est ullus"
196    " investigandi veri, nisi inveneris, et quaerendi defatigatio turpis est, cum id,"
197    " quod quaeritur, sit pulcherrimum. etenim si delectamur, cum scribimus, quis est"
198    " tam invidus, qui ab eo nos abducat?\n\nSin laboramus, quis est, qui alienae modum"
199    " statuat industriae? nam ut Terentianus Chremes non inhumanus, qui novum vicinum"
200    " non vult 'fodere aut arare aut aliquid ferre denique' -- non enim illum ab"
201    " industria, sed ab inliberali labore deterret -- sic isti curiosi, quos"
202    " offendit noster minime nobis iniucundus labor. Iis igitur est difficilius satis"
203    " facere, qui se Latina scripta dicunt contemnere.\n\nIn quibus hoc primum est in"
204    " quo admirer, cur in gravissimis rebus non delectet eos sermo patrius, cum idem"
205    " fabellas Latinas ad verbum e Graecis expressas non inviti legant. Quis enim tam"
206    " inimicus paene nomini Romano est, qui Ennii Medeam aut Antiopam Pacuvii spernat"
207    " aut reiciat, quod se isdem Euripidis fabulis delectari dicat, Latinas litteras"
208    " oderit?"
209)
210
211
212__all__ = (
213    "XThemeSelector",
214    "XThemePreview",
215)
class XThemeSelector(kvex.widgets.spinner.XSpinner):
12class XThemeSelector(XSpinner):
13    """A button for selecting the app's theme."""
14
15    def __init__(self, **kwargs):
16        """Initialize the class."""
17        kwargs = dict(option_cls=self._spinner_factory) | kwargs
18        super().__init__(
19            text=self.app.theme_name,
20            values=THEME_NAMES,
21            text_autoupdate=True,
22            **kwargs,
23        )
24        self.set_size(y="50dp")
25        self.bind(text=self._on_select)
26        self.app.bind(on_theme=self._on_app_theme)
27
28    def _on_app_theme(self, *args):
29        self.text = self.app.theme_name
30
31    def _on_select(self, *args):
32        self.app.set_theme(self.text)
33
34    def _spinner_factory(self, *args, **kwargs):
35        return XSpinnerOption(*args, subtheme_name="secondary", **kwargs)

A button for selecting the app's theme.

XThemeSelector(**kwargs)
15    def __init__(self, **kwargs):
16        """Initialize the class."""
17        kwargs = dict(option_cls=self._spinner_factory) | kwargs
18        super().__init__(
19            text=self.app.theme_name,
20            values=THEME_NAMES,
21            text_autoupdate=True,
22            **kwargs,
23        )
24        self.set_size(y="50dp")
25        self.bind(text=self._on_select)
26        self.app.bind(on_theme=self._on_app_theme)

Initialize the class.

Inherited Members
kivy.uix.spinner.Spinner
values
text_autoupdate
option_cls
dropdown_cls
is_open
sync_height
on_is_open
kivy.uix.button.Button
background_color
background_normal
background_down
background_disabled_normal
background_disabled_down
border
kivy.uix.behaviors.button.ButtonBehavior
state
last_touch
min_state_time
always_release
cancel_event
on_touch_down
on_touch_move
on_touch_up
on_press
on_release
trigger_action
kivy.uix.label.Label
texture_update
on_ref_press
disabled_color
text
text_size
base_direction
text_language
font_context
font_family
font_name
font_size
font_features
line_height
bold
italic
underline
strikethrough
padding_x
padding_y
padding
halign
valign
color
outline_width
outline_color
disabled_outline_color
texture
texture_size
mipmap
shorten
shorten_from
is_shortened
split_str
ellipsis_options
unicode_errors
markup
refs
anchors
max_lines
strip
font_hinting
font_kerning
font_blended
kivy.uix.widget.Widget
proxy_ref
apply_class_lang_rules
collide_point
collide_widget
on_motion
on_kv_post
add_widget
remove_widget
clear_widgets
register_for_motion_event
unregister_for_motion_event
export_to_png
export_as_image
get_root_window
get_parent_window
walk
walk_reverse
to_widget
to_window
to_parent
to_local
get_window_matrix
x
y
width
height
pos
size
get_right
set_right
right
get_top
set_top
top
get_center_x
set_center_x
center_x
get_center_y
set_center_y
center_y
center
cls
children
parent
size_hint_x
size_hint_y
size_hint
pos_hint
size_hint_min_x
size_hint_min_y
size_hint_min
size_hint_max_x
size_hint_max_y
size_hint_max
ids
opacity
on_opacity
canvas
get_disabled
set_disabled
inc_disabled
dec_disabled
disabled
motion_filter
kivy._event.EventDispatcher
register_event_type
unregister_event_types
unregister_event_type
is_event_type
bind
unbind
fbind
funbind
unbind_uid
get_property_observers
events
dispatch
dispatch_generic
dispatch_children
setter
getter
property
properties
create_property
apply_property
class XThemePreview(kivy.uix.behaviors.focus.FocusBehavior, kvex.widgets.layouts.XBox):
 38class XThemePreview(kv.FocusBehavior, XBox):
 39    """Widget to preview the current theme."""
 40
 41    def __init__(self):
 42        """Initialize the class."""
 43        self._palette_box = XBox()
 44        self._title_label = XLabel(
 45            font_size="24sp",
 46            color=(0, 0, 0),
 47            outline_color=(1, 1, 1),
 48            outline_width=2,
 49            valign="top",
 50            enable_theming=False,
 51        )
 52        super().__init__(orientation="vertical")
 53        self._make_widgets()
 54        self.app.bind(on_theme=self._refresh_palette_box)
 55
 56    def keyboard_on_key_down(self, w, keycode, text, modifiers):
 57        """Switch theme using arrow keys or [shift] tab."""
 58        code, key = keycode
 59        shifted = "shift" in modifiers
 60        tabbed = key == "tab"
 61        cindex = THEME_NAMES.index(self.app.theme_name)
 62        if key == "right" or (tabbed and not shifted):
 63            cindex += 1
 64        elif key == "left" or (tabbed and shifted):
 65            cindex -= 1
 66        elif key.isdigit():
 67            cindex = int(key) - 1
 68        else:
 69            return True
 70        cindex = cindex % len(THEME_NAMES)
 71        self.app.set_theme(THEME_NAMES[cindex])
 72        return True
 73
 74    def _make_widgets(self):
 75        # Palette
 76        self._palette_frame = XAnchor()
 77        self._palette_frame.add_widgets(self._palette_box, self._title_label)
 78        self._palette_frame.set_size(y="50sp")
 79        self._refresh_palette_box()
 80        # Subthemes
 81        primary = XSubThemePreview(subtheme_name="primary")
 82        primary.set_size(hy=1.5)
 83        secondary = XSubThemePreview(subtheme_name="secondary")
 84        secondary.set_size(hx=3)
 85        accent = XSubThemePreview(subtheme_name="accent")
 86        right_frame = XBox()
 87        right_frame.add_widgets(secondary, accent)
 88        outer_frame = XBox(orientation="vertical")
 89        outer_frame.add_widgets(primary, right_frame)
 90        self.add_widgets(
 91            self._palette_frame,
 92            outer_frame,
 93        )
 94
 95    def _refresh_palette_box(self, *args):
 96        self._title_label.text = self.app.theme_name.capitalize()
 97        self._palette_box.clear_widgets()
 98        for c in self.app.theme.palette:
 99            pb = XLabel(
100                text=c.hex.upper(),
101                outline_color=(0, 0, 0),
102                outline_width=2,
103                valign="bottom",
104                enable_theming=False,
105            )
106            pb.make_bg(c)
107            self._palette_box.add_widget(pb)

Widget to preview the current theme.

XThemePreview()
41    def __init__(self):
42        """Initialize the class."""
43        self._palette_box = XBox()
44        self._title_label = XLabel(
45            font_size="24sp",
46            color=(0, 0, 0),
47            outline_color=(1, 1, 1),
48            outline_width=2,
49            valign="top",
50            enable_theming=False,
51        )
52        super().__init__(orientation="vertical")
53        self._make_widgets()
54        self.app.bind(on_theme=self._refresh_palette_box)

Initialize the class.

def keyboard_on_key_down(self, w, keycode, text, modifiers):
56    def keyboard_on_key_down(self, w, keycode, text, modifiers):
57        """Switch theme using arrow keys or [shift] tab."""
58        code, key = keycode
59        shifted = "shift" in modifiers
60        tabbed = key == "tab"
61        cindex = THEME_NAMES.index(self.app.theme_name)
62        if key == "right" or (tabbed and not shifted):
63            cindex += 1
64        elif key == "left" or (tabbed and shifted):
65            cindex -= 1
66        elif key.isdigit():
67            cindex = int(key) - 1
68        else:
69            return True
70        cindex = cindex % len(THEME_NAMES)
71        self.app.set_theme(THEME_NAMES[cindex])
72        return True

Switch theme using arrow keys or [shift] tab.

Inherited Members
kivy.uix.behaviors.focus.FocusBehavior
ignored_touch
keyboard
is_focusable
focus
focused
keyboard_suggestions
focus_next
focus_previous
keyboard_mode
input_type
unfocus_on_touch
keyboard_on_textinput
on_touch_down
get_focus_next
get_focus_previous
keyboard_on_key_up
show_keyboard
hide_keyboard
kivy.uix.boxlayout.BoxLayout
spacing
padding
orientation
minimum_width
minimum_height
minimum_size
do_layout
add_widget
remove_widget
kivy.uix.layout.Layout
layout_hint_with_bounds
kivy.uix.widget.Widget
proxy_ref
apply_class_lang_rules
collide_point
collide_widget
on_motion
on_touch_move
on_touch_up
on_kv_post
clear_widgets
register_for_motion_event
unregister_for_motion_event
export_to_png
export_as_image
get_root_window
get_parent_window
walk
walk_reverse
to_widget
to_window
to_parent
to_local
get_window_matrix
x
y
width
height
pos
size
get_right
set_right
right
get_top
set_top
top
get_center_x
set_center_x
center_x
get_center_y
set_center_y
center_y
center
cls
children
parent
size_hint_x
size_hint_y
size_hint
pos_hint
size_hint_min_x
size_hint_min_y
size_hint_min
size_hint_max_x
size_hint_max_y
size_hint_max
ids
opacity
on_opacity
canvas
get_disabled
set_disabled
inc_disabled
dec_disabled
disabled
motion_filter
kivy._event.EventDispatcher
register_event_type
unregister_event_types
unregister_event_type
is_event_type
bind
unbind
fbind
funbind
unbind_uid
get_property_observers
events
dispatch
dispatch_generic
dispatch_children
setter
getter
property
properties
create_property
apply_property