1# Copyright 2022 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"""Wrapper fuctions to add borders around prompt_toolkit containers.""" 15 16from typing import Callable 17 18from prompt_toolkit.layout import ( 19 AnyContainer, 20 FormattedTextControl, 21 HSplit, 22 VSplit, 23 Window, 24) 25 26from prompt_toolkit.formatted_text import StyleAndTextTuples 27 28 29def create_border( 30 # pylint: disable=too-many-arguments 31 content: AnyContainer, 32 content_height: int | None = None, 33 title: Callable[[], str | StyleAndTextTuples] | str = '', 34 border_style: Callable[[], str] | str = '', 35 base_style: Callable[[], str] | str = '', 36 top: bool = True, 37 bottom: bool = True, 38 left: bool = True, 39 right: bool = True, 40 horizontal_char: str = '━', 41 vertical_char: str = '┃', 42 top_left_char: str = '┏', 43 top_right_char: str = '┓', 44 bottom_left_char: str = '┗', 45 bottom_right_char: str = '┛', 46 left_margin_columns: int = 0, 47 right_margin_columns: int = 0, 48) -> HSplit: 49 """Wrap any prompt_toolkit container in a border.""" 50 51 top_border_items: list[AnyContainer] = [] 52 if left: 53 top_border_items.append( 54 Window(width=1, height=1, char=top_left_char, style=border_style) 55 ) 56 57 title_text = None 58 if title: 59 if isinstance(title, str): 60 title_text = FormattedTextControl( 61 [('', f'{horizontal_char}{horizontal_char} {title} ')] 62 ) 63 else: 64 title_text = FormattedTextControl(title) 65 66 top_border_items.append( 67 Window( 68 title_text, 69 char=horizontal_char, 70 # Expand width to max available space 71 dont_extend_width=False, 72 style=border_style, 73 ) 74 ) 75 if right: 76 top_border_items.append( 77 Window(width=1, height=1, char=top_right_char, style=border_style) 78 ) 79 80 content_items: list[AnyContainer] = [] 81 if left: 82 content_items.append( 83 Window( 84 width=1, 85 height=content_height, 86 char=vertical_char, 87 style=border_style, 88 ) 89 ) 90 91 if left_margin_columns > 0: 92 content_items.append( 93 Window( 94 width=left_margin_columns, 95 height=content_height, 96 char=' ', 97 style=border_style, 98 ) 99 ) 100 content_items.append(content) 101 if right_margin_columns > 0: 102 content_items.append( 103 Window( 104 width=right_margin_columns, 105 height=content_height, 106 char=' ', 107 style=border_style, 108 ) 109 ) 110 111 if right: 112 content_items.append( 113 Window(width=1, height=2, char=vertical_char, style=border_style) 114 ) 115 116 bottom_border_items: list[AnyContainer] = [] 117 if left: 118 bottom_border_items.append( 119 Window(width=1, height=1, char=bottom_left_char) 120 ) 121 bottom_border_items.append( 122 Window( 123 char=horizontal_char, 124 # Expand width to max available space 125 dont_extend_width=False, 126 ) 127 ) 128 if right: 129 bottom_border_items.append( 130 Window(width=1, height=1, char=bottom_right_char) 131 ) 132 133 rows: list[AnyContainer] = [] 134 if top: 135 rows.append(VSplit(top_border_items, height=1, padding=0)) 136 rows.append(VSplit(content_items, height=content_height)) 137 if bottom: 138 rows.append( 139 VSplit(bottom_border_items, height=1, padding=0, style=border_style) 140 ) 141 142 return HSplit(rows, style=base_style) 143