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"""LogPane FilterToolbar class.""" 15 16from __future__ import annotations 17import functools 18from typing import TYPE_CHECKING 19 20from prompt_toolkit.filters import Condition 21from prompt_toolkit.layout import ( 22 ConditionalContainer, 23 FormattedTextControl, 24 VSplit, 25 Window, 26 WindowAlign, 27 HorizontalAlign, 28) 29from prompt_toolkit.mouse_events import MouseEvent, MouseEventType 30 31from pw_console.style import ( 32 get_button_style, 33 get_toolbar_style, 34) 35from pw_console.widgets import ( 36 mouse_handlers, 37 to_keybind_indicator, 38) 39 40if TYPE_CHECKING: 41 from pw_console.log_pane import LogPane 42 43 44class FilterToolbar(ConditionalContainer): 45 """Container showing each filter applied in order.""" 46 47 TOOLBAR_HEIGHT = 1 48 49 def mouse_handler_delete_filter(self, filter_text, mouse_event: MouseEvent): 50 """Delete the given log filter.""" 51 if mouse_event.event_type == MouseEventType.MOUSE_UP: 52 self.log_pane.log_view.delete_filter(filter_text) 53 return None 54 return NotImplemented 55 56 def get_left_fragments(self): 57 """Return formatted text tokens for display.""" 58 separator = ('', ' ') 59 space = ('', ' ') 60 fragments = [('class:filter-bar-title', ' Filters '), separator] 61 62 button_style = get_button_style(self.log_pane) 63 64 for filter_text, log_filter in self.log_pane.log_view.filters.items(): 65 fragments.append(('class:filter-bar-delimiter', '<')) 66 67 if log_filter.invert: 68 fragments.append(('class:filter-bar-setting', 'NOT ')) 69 70 if log_filter.field: 71 fragments.append(('class:filter-bar-setting', log_filter.field)) 72 fragments.append(space) 73 74 fragments.append(('', filter_text)) 75 fragments.append(space) 76 77 fragments.append( 78 ( 79 button_style + ' class:filter-bar-delete', 80 ' (X) ', 81 functools.partial( 82 self.mouse_handler_delete_filter, filter_text 83 ), 84 ) 85 ) # type: ignore 86 fragments.append(('class:filter-bar-delimiter', '>')) 87 88 fragments.append(separator) 89 return fragments 90 91 def get_center_fragments(self): 92 """Return formatted text tokens for display.""" 93 clear_filters = functools.partial( 94 mouse_handlers.on_click, 95 self.log_pane.log_view.clear_filters, 96 ) 97 98 button_style = get_button_style(self.log_pane) 99 100 return to_keybind_indicator( 101 'Ctrl-Alt-r', 102 'Clear Filters', 103 clear_filters, 104 base_style=button_style, 105 ) 106 107 def __init__(self, log_pane: LogPane): 108 self.log_pane = log_pane 109 left_bar_control = FormattedTextControl(self.get_left_fragments) 110 left_bar_window = Window( 111 content=left_bar_control, 112 align=WindowAlign.LEFT, 113 dont_extend_width=True, 114 ) 115 center_bar_control = FormattedTextControl(self.get_center_fragments) 116 center_bar_window = Window( 117 content=center_bar_control, 118 align=WindowAlign.LEFT, 119 dont_extend_width=False, 120 ) 121 super().__init__( 122 VSplit( 123 [ 124 left_bar_window, 125 center_bar_window, 126 ], 127 style=functools.partial( 128 get_toolbar_style, self.log_pane, dim=True 129 ), 130 height=1, 131 align=HorizontalAlign.LEFT, 132 ), 133 # Only show if filtering is enabled. 134 filter=Condition(lambda: self.log_pane.log_view.filtering_on), 135 ) 136