xref: /aosp_15_r20/external/pigweed/pw_console/py/pw_console/widgets/border.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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