• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2
3# Copyright 2021 The Pigweed Authors
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9#     https://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16"""Functions to create checkboxes for menus and toolbars."""
17
18import sys
19from typing import Callable, Iterable, Optional, NamedTuple
20
21from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple
22from prompt_toolkit.formatted_text import StyleAndTextTuples
23
24_KEY_SEPARATOR = ' '
25_CHECKED_BOX = '[✓]'
26
27if sys.platform in ['win32']:
28    _CHECKED_BOX = '[x]'
29
30
31class ToolbarButton(NamedTuple):
32    key: Optional[str] = None
33    description: Optional[str] = 'Button'
34    mouse_handler: Optional[Callable] = None
35    is_checkbox: bool = False
36    checked: Optional[Callable] = None
37
38
39def to_checkbox(
40    checked: bool,
41    mouse_handler: Optional[Callable] = None,
42    end: str = ' ',
43    unchecked_style: str = 'class:checkbox',
44    checked_style: str = 'class:checkbox-checked',
45) -> OneStyleAndTextTuple:
46    text = _CHECKED_BOX if checked else '[ ]'
47    text += end
48    style = checked_style if checked else unchecked_style
49    if mouse_handler:
50        return (style, text, mouse_handler)
51    return (style, text)
52
53
54def to_checkbox_text(checked: bool, end=' '):
55    return to_checkbox(checked, end=end)[1]
56
57
58def to_setting(
59    checked: bool,
60    text: str,
61    active_style='class:toolbar-setting-active',
62    inactive_style='',
63    mouse_handler=None,
64):
65    """Apply a style to text if checked is True."""
66    style = active_style if checked else inactive_style
67    if mouse_handler:
68        return (style, text, mouse_handler)
69    return (style, text)
70
71
72def to_checkbox_with_keybind_indicator(
73    checked: bool,
74    key: str,
75    description: str,
76    mouse_handler=None,
77    base_style: str = '',
78    **checkbox_kwargs,
79):
80    """Create a clickable keybind indicator with checkbox for toolbars."""
81    if mouse_handler:
82        return to_keybind_indicator(key,
83                                    description,
84                                    mouse_handler,
85                                    leading_fragments=[
86                                        to_checkbox(checked, mouse_handler,
87                                                    **checkbox_kwargs)
88                                    ],
89                                    base_style=base_style)
90    return to_keybind_indicator(
91        key,
92        description,
93        leading_fragments=[to_checkbox(checked, **checkbox_kwargs)],
94        base_style=base_style)
95
96
97def to_keybind_indicator(
98    key: str,
99    description: str,
100    mouse_handler: Optional[Callable] = None,
101    leading_fragments: Optional[Iterable] = None,
102    middle_fragments: Optional[Iterable] = None,
103    base_style: str = '',
104    key_style: str = 'class:keybind',
105    description_style: str = 'class:keyhelp',
106):
107    """Create a clickable keybind indicator for toolbars."""
108    if base_style:
109        base_style += ' '
110
111    fragments: StyleAndTextTuples = []
112    fragments.append((base_style + 'class:toolbar-button-decoration', ' '))
113
114    def append_fragment_with_base_style(frag_list, fragment) -> None:
115        if mouse_handler:
116            frag_list.append(
117                (base_style + fragment[0], fragment[1], mouse_handler))
118        else:
119            frag_list.append((base_style + fragment[0], fragment[1]))
120
121    # Add any starting fragments first
122    if leading_fragments:
123        for fragment in leading_fragments:
124            append_fragment_with_base_style(fragments, fragment)
125
126    # Function name
127    if mouse_handler:
128        fragments.append(
129            (base_style + description_style, description, mouse_handler))
130    else:
131        fragments.append((base_style + description_style, description))
132
133    if middle_fragments:
134        for fragment in middle_fragments:
135            append_fragment_with_base_style(fragments, fragment)
136
137    # Separator and keybind
138    if key:
139        if mouse_handler:
140            fragments.append((base_style + description_style, _KEY_SEPARATOR,
141                              mouse_handler))
142            fragments.append((base_style + key_style, key, mouse_handler))
143        else:
144            fragments.append((base_style + description_style, _KEY_SEPARATOR))
145            fragments.append((base_style + key_style, key))
146
147    fragments.append((base_style + 'class:toolbar-button-decoration', ' '))
148    return fragments
149