blob: 17f610345d00db420900bdf4a1f67a137e2afb5e [file] [log] [blame]
# Copyright 2021 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
from typing import TYPE_CHECKING
from prompt_toolkit.filters import (
Condition,
has_focus,
)
from prompt_toolkit.layout import (
ConditionalContainer,
FormattedTextControl,
VSplit,
Window,
WindowAlign,
HorizontalAlign,
)
from prompt_toolkit.data_structures import Point
import pw_console.widgets.checkbox
import pw_console.widgets.mouse_handlers
import pw_console.style
if TYPE_CHECKING:
from pw_console.log_pane import LogPane
class LineInfoBar(ConditionalContainer):
"""One line bar for showing current and total log lines."""
@staticmethod
def get_tokens(log_pane: 'LogPane'):
"""Return formatted text tokens for display."""
tokens = ' Line {} / {} '.format(
log_pane.log_view.get_current_line() + 1,
log_pane.log_view.get_total_count(),
)
return [('', tokens)]
def __init__(self, log_pane: 'LogPane'):
info_bar_control = FormattedTextControl(
functools.partial(LineInfoBar.get_tokens, log_pane))
info_bar_window = Window(content=info_bar_control,
align=WindowAlign.RIGHT,
dont_extend_width=True)
super().__init__(
VSplit([info_bar_window],
height=1,
style=functools.partial(pw_console.style.get_toolbar_style,
log_pane,
dim=True),
align=HorizontalAlign.RIGHT),
# Only show current/total line info if not auto-following
# logs. Similar to tmux behavior.
filter=Condition(lambda: not log_pane.log_view.follow))
class TableToolbar(ConditionalContainer):
"""One line toolbar for showing table headers."""
TOOLBAR_HEIGHT = 1
def __init__(self, log_pane: 'LogPane'):
# FormattedText of the table column headers.
table_header_bar_control = FormattedTextControl(
log_pane.log_view.render_table_header,
get_cursor_position=lambda: Point(
log_pane.get_horizontal_scroll_amount(), 0))
# Left justify the header content.
table_header_bar_window = Window(
content=table_header_bar_control,
align=WindowAlign.LEFT,
dont_extend_width=False,
get_horizontal_scroll=log_pane.get_horizontal_scroll_amount,
)
super().__init__(
VSplit([table_header_bar_window],
height=1,
style=functools.partial(pw_console.style.get_toolbar_style,
log_pane,
dim=True),
align=HorizontalAlign.LEFT),
filter=Condition(lambda: log_pane.table_view and log_pane.log_view.
get_total_count() > 0))
class BottomToolbarBar(ConditionalContainer):
"""One line toolbar for display at the bottom of the LogPane."""
TOOLBAR_HEIGHT = 1
def get_left_text_tokens(self):
"""Return toolbar indicator and title."""
title = ' {} '.format(self.log_pane.pane_title())
focus = functools.partial(pw_console.widgets.mouse_handlers.on_click,
self.log_pane.focus_self)
return pw_console.style.get_pane_indicator(self.log_pane, title, focus)
def get_center_text_tokens(self):
"""Return formatted text tokens for display in the center part of the
toolbar."""
# Create mouse handler functions.
focus = functools.partial(pw_console.widgets.mouse_handlers.on_click,
self.log_pane.focus_self)
toggle_wrap_lines = functools.partial(
pw_console.widgets.mouse_handlers.on_click,
self.log_pane.toggle_wrap_lines)
clear_history = functools.partial(
pw_console.widgets.mouse_handlers.on_click,
self.log_pane.clear_history)
toggle_follow = functools.partial(
pw_console.widgets.mouse_handlers.on_click,
self.log_pane.toggle_follow)
toggle_table_view = functools.partial(
pw_console.widgets.mouse_handlers.on_click,
self.log_pane.toggle_table_view)
start_search = functools.partial(
pw_console.widgets.mouse_handlers.on_click,
self.log_pane.start_search)
copy_lines = functools.partial(
pw_console.widgets.mouse_handlers.on_click,
self.log_pane.log_view.copy_visible_lines)
button_style = pw_console.style.get_button_style(self.log_pane)
# FormattedTextTuple contents: (Style, Text, Mouse handler)
separator_text = [('', ' ', focus)
] # 2 spaces of separaton between keybinds.
fragments = []
fragments.extend(separator_text)
fragments.extend(
pw_console.widgets.checkbox.to_keybind_indicator(
'/', 'Search', start_search, base_style=button_style))
fragments.extend(separator_text)
fragments.extend(
pw_console.widgets.checkbox.to_keybind_indicator(
'Ctrl-c', 'Copy Lines', copy_lines, base_style=button_style))
fragments.extend(separator_text)
fragments.extend(
pw_console.widgets.checkbox.to_checkbox_with_keybind_indicator(
self.log_pane.log_view.follow,
'f',
'Follow',
toggle_follow,
base_style=button_style))
fragments.extend(separator_text)
fragments.extend(
pw_console.widgets.checkbox.to_checkbox_with_keybind_indicator(
self.log_pane.table_view,
't',
'Table',
toggle_table_view,
base_style=button_style))
fragments.extend(separator_text)
fragments.extend(
pw_console.widgets.checkbox.to_checkbox_with_keybind_indicator(
self.log_pane.wrap_lines,
'w',
'Wrap',
toggle_wrap_lines,
base_style=button_style))
fragments.extend(separator_text)
fragments.extend(
pw_console.widgets.checkbox.to_keybind_indicator(
'C', 'Clear', clear_history, base_style=button_style))
fragments.extend(separator_text)
# Remaining whitespace should focus on click.
fragments.append(('', ' ', focus))
return fragments
def get_right_text_tokens(self):
"""Return formatted text tokens for display."""
focus = functools.partial(pw_console.widgets.mouse_handlers.on_click,
self.log_pane.focus_self)
fragments = []
if not has_focus(self.log_pane.__pt_container__())():
fragments.append((
'class:toolbar-button-inactive class:toolbar-button-decoration',
' ', focus))
fragments.append(('class:toolbar-button-inactive class:keyhelp',
'click to focus', focus))
fragments.append((
'class:toolbar-button-inactive class:toolbar-button-decoration',
' ', focus))
fragments.append(
('', ' {} '.format(self.log_pane.pane_subtitle()), focus))
return fragments
def __init__(self, log_pane: 'LogPane'):
self.log_pane = log_pane
title_section_window = Window(
content=FormattedTextControl(
# Callable to get formatted text tuples.
self.get_left_text_tokens),
align=WindowAlign.LEFT,
dont_extend_width=True,
)
keybind_section_window = Window(
content=FormattedTextControl(
# Callable to get formatted text tuples.
self.get_center_text_tokens),
align=WindowAlign.LEFT,
dont_extend_width=False,
)
log_source_name = Window(
content=FormattedTextControl(
# Callable to get formatted text tuples.
self.get_right_text_tokens),
# Right side text should appear at the far right of the toolbar
align=WindowAlign.RIGHT,
dont_extend_width=True,
)
toolbar_vsplit = VSplit(
[
title_section_window,
keybind_section_window,
log_source_name,
],
height=BottomToolbarBar.TOOLBAR_HEIGHT,
style=functools.partial(pw_console.style.get_toolbar_style,
log_pane),
)
# ConditionalContainer init()
super().__init__(
# Contents
toolbar_vsplit,
filter=Condition(lambda: log_pane.show_bottom_toolbar),
)