-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8065b37
commit 8775d89
Showing
3 changed files
with
159 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
"""Display a toolbar in each file tab.""" | ||
from __future__ import annotations | ||
|
||
import dataclasses | ||
import logging | ||
import tkinter | ||
from functools import partial | ||
from tkinter import ttk | ||
from typing import Any, Callable, Iterable | ||
|
||
from porcupine import get_tab_manager, tabs | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
setup_after = ["filetypes"] | ||
|
||
|
||
# TODO: add icon (make text optional?) | ||
@dataclasses.dataclass(kw_only=True) | ||
class Button: | ||
text: str | ||
description: str | None = None | ||
command: Callable | ||
|
||
|
||
@dataclasses.dataclass(kw_only=True) | ||
class ButtonGroup: | ||
name: str | ||
priority: int # 0 is highest | ||
buttons: list[Button] | ||
separator: bool = True | ||
|
||
|
||
class SortedButtonGroupList(list[ButtonGroup]): | ||
"""A of button groups that sorts itself automatically""" | ||
|
||
# no this wasn't necessary, but I am in too deep to stop now | ||
# but seriously why isn't there a sorted list type in the stdlib? | ||
@classmethod | ||
def _key_func(cls, group: ButtonGroup) -> int: | ||
return group.priority | ||
|
||
def __init__(self, *args: Iterable[ButtonGroup], **kwargs: ButtonGroup) -> None: | ||
super().__init__(*args, **kwargs) | ||
self.sort(key=self._key_func) | ||
|
||
def append(self, __item: ButtonGroup) -> None: | ||
super().append(__item) | ||
self.sort(key=self._key_func) | ||
|
||
def extend(self, __iterable: Iterable[ButtonGroup]) -> None: | ||
super().extend(__iterable) | ||
self.sort(key=self._key_func) | ||
|
||
|
||
filetype_button_groups_mapping: dict[str, SortedButtonGroupList] = {} | ||
|
||
|
||
def add_button_group( | ||
*, filetype_name: str, name: str, buttons: list[Button], priority: int = 0, separator=True | ||
) -> None: | ||
button_group = ButtonGroup(name=name, priority=priority, buttons=buttons, separator=separator) | ||
if filetype_button_groups_mapping.get(filetype_name): | ||
filetype_button_groups_mapping[filetype_name].append(button_group) | ||
else: | ||
filetype_button_groups_mapping[filetype_name] = SortedButtonGroupList([button_group]) | ||
|
||
|
||
class ToolBar(ttk.Frame): | ||
def __init__(self, tab: tabs.FileTab): | ||
super().__init__(tab.top_frame, name="toolbar", border=1, relief="raised") | ||
self._tab = tab | ||
|
||
def update_buttons(self, tab: tabs.FileTab, junk: object = None) -> None: | ||
"""Different filetypes have different buttons associated with them.""" | ||
filetype_name = tab.settings.get("filetype_name", object) | ||
button_groups = filetype_button_groups_mapping.get(filetype_name) | ||
if not button_groups: | ||
return | ||
|
||
for button_group in button_groups: | ||
for button in button_group.buttons: | ||
ttk.Button( | ||
self, | ||
command=partial(button.command, tab), | ||
style="Statusbar.TButton", | ||
text=button.text, | ||
).pack(side="left", padx=10, pady=5) | ||
|
||
|
||
def on_new_filetab(tab: tabs.FileTab) -> None: | ||
toolbar = ToolBar(tab) | ||
toolbar.pack(side="bottom", fill="x") | ||
|
||
tab.bind("<<TabSettingChanged:filetype_name>>", partial(toolbar.update_buttons, tab), add=True) | ||
toolbar.update_buttons(tab) | ||
|
||
|
||
def update_button_style(junk_event: object = None) -> None: | ||
# https://tkdocs.com/tutorial/styles.html | ||
# tkinter's style stuff sucks | ||
get_tab_manager().tk.eval( | ||
"ttk::style configure Statusbar.TButton -padding {10 0} -anchor center" | ||
) | ||
|
||
|
||
def setup() -> None: | ||
get_tab_manager().add_filetab_callback(on_new_filetab) | ||
get_tab_manager().bind("<<ThemeChanged>>", update_button_style, add=True) | ||
update_button_style() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from __future__ import annotations | ||
|
||
from porcupine.plugins import toolbar | ||
|
||
|
||
def _gen_button_group(priority: int, name: str | None = None) -> toolbar.ButtonGroup: | ||
return toolbar.ButtonGroup( | ||
name=f"priority = {priority}" if not name else name, priority=priority, buttons=[] | ||
) | ||
|
||
|
||
def test_manual_sorted_button_group_list_with_append_and_extend(): | ||
# reverse order | ||
sorted_button_group_list = toolbar.SortedButtonGroupList( | ||
[_gen_button_group(i) for i in [100, 0, 50, 25, 2, 1, 99]] | ||
) | ||
|
||
sorted_button_group_list.append(_gen_button_group(33)) | ||
sorted_button_group_list.append(_gen_button_group(5)) | ||
|
||
sorted_button_group_list.extend( | ||
[_gen_button_group(9), _gen_button_group(8), _gen_button_group(7)] | ||
) | ||
|
||
for i, button_group in zip( | ||
[0, 1, 2, 5, 7, 8, 9, 25, 33, 50, 99, 100], sorted_button_group_list | ||
): | ||
assert i == button_group.priority | ||
|
||
|
||
def test_big_reversed_sorted_button_group_list(): | ||
qty = 100 | ||
# reverse order | ||
sorted_button_group_list = toolbar.SortedButtonGroupList( | ||
[_gen_button_group(i) for i in reversed(range(qty))] | ||
) | ||
|
||
for i, button_group in zip(range(qty), sorted_button_group_list): | ||
assert i == button_group.priority |