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