blob: 6195dd2fb8ac233bfd0d126a4d2e79450dd89639 [file] [log] [blame]
# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""LogPane Info Toolbar classes."""
from __future__ import annotations
import functools
import logging
import sys
from typing import Optional, Callable, TYPE_CHECKING
from prompt_toolkit.data_structures import Point
from prompt_toolkit.key_binding import KeyBindings, KeyPressEvent
from prompt_toolkit.filters import Condition
from prompt_toolkit.layout import (
ConditionalContainer,
FormattedTextControl,
HSplit,
Window,
WindowAlign,
)
from pw_console.widgets import (
create_border,
mouse_handlers,
to_keybind_indicator,
)
if TYPE_CHECKING:
from pw_console.console_app import ConsoleApp
_LOG = logging.getLogger(__package__)
class QuitDialog(ConditionalContainer):
"""Confirmation quit dialog box."""
DIALOG_HEIGHT = 2
def __init__(
self, application: ConsoleApp, on_quit: Optional[Callable] = None
):
self.application = application
self.show_dialog = False
# Tracks the last focused container, to enable restoring focus after
# closing the dialog.
self.last_focused_pane = None
self.on_quit_function = (
on_quit if on_quit else self._default_on_quit_function
)
# Quit keybindings are active when this dialog is in focus
key_bindings = KeyBindings()
register = self.application.prefs.register_keybinding
@register('quit-dialog.yes', key_bindings)
def _quit(_event: KeyPressEvent) -> None:
"""Close save as bar."""
self.quit_action()
@register('quit-dialog.no', key_bindings)
def _cancel(_event: KeyPressEvent) -> None:
"""Close save as bar."""
self.close_dialog()
self.exit_message = 'Quit? y/n '
action_bar_control = FormattedTextControl(
self.get_action_fragments,
show_cursor=True,
focusable=True,
key_bindings=key_bindings,
# Cursor will appear after the exit_message
get_cursor_position=lambda: Point(len(self.exit_message), 0),
)
action_bar_window = Window(
content=action_bar_control,
height=QuitDialog.DIALOG_HEIGHT,
align=WindowAlign.LEFT,
dont_extend_width=False,
)
super().__init__(
create_border(
HSplit(
[action_bar_window],
height=QuitDialog.DIALOG_HEIGHT,
style='class:quit-dialog',
),
QuitDialog.DIALOG_HEIGHT,
border_style='class:quit-dialog-border',
left_margin_columns=1,
),
filter=Condition(lambda: self.show_dialog),
)
def focus_self(self):
self.application.layout.focus(self)
def close_dialog(self):
"""Close this dialog box."""
self.show_dialog = False
# Restore original focus if possible.
if self.last_focused_pane:
self.application.layout.focus(self.last_focused_pane)
else:
# Fallback to focusing on the main menu.
self.application.focus_main_menu()
def open_dialog(self):
self.show_dialog = True
self.last_focused_pane = self.application.focused_window()
self.focus_self()
self.application.redraw_ui()
def _default_on_quit_function(self):
if hasattr(self.application, 'application'):
self.application.application.exit()
else:
sys.exit()
def quit_action(self):
self.on_quit_function()
def get_action_fragments(self):
"""Return FormattedText with action buttons."""
# Mouse handlers
focus = functools.partial(mouse_handlers.on_click, self.focus_self)
cancel = functools.partial(mouse_handlers.on_click, self.close_dialog)
quit_action = functools.partial(
mouse_handlers.on_click, self.quit_action
)
# Separator should have the focus mouse handler so clicking on any
# whitespace focuses the input field.
separator_text = ('', ' ', focus)
# Default button style
button_style = 'class:toolbar-button-inactive'
fragments = [('', self.exit_message), separator_text]
fragments.append(('', '\n'))
# Cancel button
fragments.extend(
to_keybind_indicator(
key='n / Ctrl-c',
description='Cancel',
mouse_handler=cancel,
base_style=button_style,
)
)
# Two space separator
fragments.append(separator_text)
# Save button
fragments.extend(
to_keybind_indicator(
key='y / Ctrl-d',
description='Quit',
mouse_handler=quit_action,
base_style=button_style,
)
)
# One space separator
fragments.append(('', ' ', focus))
return fragments