Skip to content

Commit

Permalink
Move selectorwin attribute logic out of ui_tk
Browse files Browse the repository at this point in the history
  • Loading branch information
TeamSpen210 committed Jul 9, 2024
1 parent 1daabea commit 070968c
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 66 deletions.
102 changes: 69 additions & 33 deletions src/app/selector_win.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import tkinter as tk

from contextlib import aclosing
from collections.abc import Callable, Container, Iterable
from collections.abc import Callable, Container, Iterable, Iterator
from collections import defaultdict
from enum import Enum, auto as enum_auto
import functools
Expand All @@ -28,8 +28,6 @@

from app.mdown import MarkdownData
from app import sound, img, DEV_MODE
from ui_tk.tooltip import set_tooltip
from ui_tk.img import TK_IMG
from ui_tk.wid_transtoken import set_menu_text, set_text
from ui_tk import TK_ROOT, tk_tools
from packages import SelitemData, AttrTypes, AttrDef as AttrDef, AttrMap
Expand Down Expand Up @@ -275,7 +273,6 @@ class SelectorWinBase[ButtonT, SuggLblT]:
modal: bool
win: tk.Toplevel # TODO move
attrs: list[AttrDef]
attr_labels: dict[str, ttk.Label]

# Current list of item IDs we display.
item_list: list[utils.SpecialID]
Expand Down Expand Up @@ -342,6 +339,7 @@ def __init__(self, opt: Options) -> None:
self.description = opt.desc
self.readonly_description = opt.readonly_desc
self.readonly_override = opt.readonly_override
self.attrs = list(opt.attributes)

prev_state = config.APP.get_cur_conf(
LastSelected,
Expand Down Expand Up @@ -521,6 +519,30 @@ def sort_func(item_id: utils.SpecialID) -> str:
assert menu_pos is not None, "Didn't add to the menu?"
group.menu_pos = menu_pos

def _attr_widget_positions(self) -> Iterator[tuple[
AttrDef, int,
Literal['left', 'right', 'wide'],
]]:
"""Positions all the required attribute widgets.
Yields (attr, row, col_type) tuples.
"""
self.attrs.sort(key=lambda at: 0 if at.type.is_wide else 1)
index = 0
for attr in self.attrs:
# Wide ones have their own row, narrow ones are two to a row.
if attr.type.is_wide:
if index % 2: # Row has a single narrow, skip the empty space.
index += 1
yield attr, index // 2, 'wide'
index += 2
else:
if index % 2:
yield attr, index // 2, 'right'
else:
yield attr, index // 2, 'left'
index += 1

async def _rollover_suggest_task(self) -> None:
"""Handle previewing suggested items when hovering over the 'set suggested' button."""
while True:
Expand Down Expand Up @@ -796,35 +818,34 @@ def sel_item(self, item_id: utils.SpecialID, _: object = None) -> None:
item_attrs = EmptyMapping
for attr in self.attrs:
val = item_attrs.get(attr.id, attr.default)
attr_label = self.attr_labels[attr.id]

if attr.type is AttrTypes.BOOL:
TK_IMG.apply(attr_label, ICON_CHECK if val else ICON_CROSS)
elif attr.type is AttrTypes.COLOR:
assert isinstance(val, Vec)
TK_IMG.apply(attr_label, img.Handle.color(val, 16, 16))
# Display the full color when hovering...
set_tooltip(attr_label, TRANS_ATTR_COLOR.format(
r=int(val.x), g=int(val.y), b=int(val.z),
))
elif attr.type.is_list:
# Join the values (in alphabetical order)
assert isinstance(val, Iterable) and not isinstance(val, Vec), repr(val)
children = [
txt if isinstance(txt, TransToken) else TransToken.untranslated(txt)
for txt in val
]
if attr.type is AttrTypes.LIST_AND:
set_text(attr_label, TransToken.list_and(children, sort=True))
else:
set_text(attr_label, TransToken.list_or(children, sort=True))
elif attr.type is AttrTypes.STRING:
# Just a string.
if not isinstance(val, TransToken):
val = TransToken.untranslated(str(val))
set_text(attr_label, val)
else:
raise ValueError(f'Invalid attribute type: "{attr.type}"')
match attr.type:
case AttrTypes.BOOL:
self._ui_attr_set_image(attr, ICON_CHECK if val else ICON_CROSS)
case AttrTypes.COLOUR:
assert isinstance(val, Vec)
self._ui_attr_set_image(attr, img.Handle.color(val, 16, 16))
# Display the full color when hovering...
self._ui_attr_set_tooltip(attr, TRANS_ATTR_COLOR.format(
r=int(val.x), g=int(val.y), b=int(val.z),
))
case AttrTypes.LIST_OR | AttrTypes.LIST_AND:
# Join the values (in alphabetical order)
assert isinstance(val, Iterable) and not isinstance(val, Vec), repr(val)
children = [
txt if isinstance(txt, TransToken) else TransToken.untranslated(txt)
for txt in val
]
if attr.type is AttrTypes.LIST_AND:
self._ui_attr_set_text(attr, TransToken.list_and(children, sort=True))
else:
self._ui_attr_set_text(attr, TransToken.list_or(children, sort=True))
case AttrTypes.STRING:
# Just a string.
if not isinstance(val, TransToken):
val = TransToken.untranslated(str(val))
self._ui_attr_set_text(attr, val)
case _:
assert_never(attr.type)

def key_navigate(self, key: NavKeys) -> None:
"""Navigate using arrow keys."""
Expand Down Expand Up @@ -1176,6 +1197,21 @@ def _ui_props_set_samp_button_icon(self, glyph: str, /) -> None:
"""Set the icon in the play-sample button."""
raise NotImplementedError

@abstractmethod
def _ui_attr_set_text(self, attr: AttrDef, text: TransToken, /) -> None:
"""Set the value of a text-style attribute widget."""
raise NotImplementedError

@abstractmethod
def _ui_attr_set_image(self, attr: AttrDef, image: img.Handle, /) -> None:
"""Set the image for an image-style attribute widget."""
raise NotImplementedError

@abstractmethod
def _ui_attr_set_tooltip(self, attr: AttrDef, tooltip: TransToken, /) -> None:
"""Set the hover tooltip. This only applies to image-style widgets."""
raise NotImplementedError

@abstractmethod
def _ui_menu_set_font(self, item_id: utils.SpecialID, /, suggested: bool) -> None:
"""Set the font of an item, and its parent group."""
Expand Down
14 changes: 7 additions & 7 deletions src/packages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,29 +119,29 @@
@utils.freeze_enum_props
class AttrTypes(Enum):
"""The type of labels used for selectoritem attributes."""
STR = STRING = 'string' # Normal text
STRING = 'string' # Normal text
LIST_AND = 'list_and' # A sequence, joined by commas
LIST_OR = 'list_or' # A sequence, joined by commas
BOOL = 'bool' # A yes/no checkmark
COLOR = COLOUR = 'color' # A Vec 0-255 RGB colour
COLOUR = 'color' # A Vec 0-255 RGB colour

@property
def is_wide(self) -> bool:
"""Determine if this should be placed on its own row, or paired with another."""
return self.value in ('string', 'list_and', 'list_or')

@property
def is_list(self) -> bool:
"""Determine if this is a list."""
return self.value.startswith('list_')
def is_image(self) -> bool:
"""Check if this uses an image, or is just text."""
return self.value in ('bool', 'color')


# TransToken is str()-ified.
type AttrValues = str | TransToken | Iterable[str | TransToken] | bool | Vec
type AttrMap = Mapping[str, AttrValues]


@attrs.define
@attrs.define(eq=False)
class AttrDef:
"""Configuration for attributes shown on selector labels."""
id: str
Expand Down Expand Up @@ -198,7 +198,7 @@ def color(
"""Alternative constructor for color-type attrs."""
if default is None:
default = Vec(255, 255, 255)
return AttrDef(attr_id, desc, default, AttrTypes.COLOR)
return AttrDef(attr_id, desc, default, AttrTypes.COLOUR)


@attrs.frozen(kw_only=True)
Expand Down
63 changes: 37 additions & 26 deletions src/ui_tk/selector_win.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class SelectorWin(SelectorWinBase[
prop_desc: RichText
prop_scroll: tk_tools.HidingScroll
prop_reset: ttk.Button
attr_labels: dict[AttrDef, ttk.Label]

# Variable associated with self.display.
disp_label: tk.StringVar
Expand Down Expand Up @@ -302,7 +303,6 @@ def __init__(self, parent: tk.Tk | tk.Toplevel, opt: Options) -> None:
)

# Wide before short.
self.attrs = sorted(opt.attributes, key=lambda at: 0 if at.type.is_wide else 1)
self.attr_labels = {}
if self.attrs:
attrs_frame = ttk.Frame(self.prop_frm)
Expand All @@ -314,17 +314,15 @@ def __init__(self, parent: tk.Tk | tk.Toplevel, opt: Options) -> None:
padx=5,
)
attrs_frame.columnconfigure(0, weight=1)
attrs_frame.columnconfigure(1, weight=1)
attrs_frame.columnconfigure(2, weight=1)

# Add in all the attribute labels
index = 0
for attr in self.attrs:
for attr, row, col_type in self._attr_widget_positions():
attr_frame = ttk.Frame(attrs_frame)
desc_label = ttk.Label(attr_frame)
set_text(desc_label, TRANS_ATTR_DESC.format(desc=attr.desc))
self.attr_labels[attr.id] = attr_label = ttk.Label(attr_frame)
self.attr_labels[attr] = attr_label = ttk.Label(attr_frame)

if attr.type is AttrTypes.COLOR:
if attr.type is AttrTypes.COLOUR:
# A small colour swatch.
attr_label.configure(relief='raised')
# Show the color value when hovered.
Expand All @@ -333,30 +331,28 @@ def __init__(self, parent: tk.Tk | tk.Toplevel, opt: Options) -> None:
desc_label.grid(row=0, column=0, sticky='e')
attr_label.grid(row=0, column=1, sticky='w')
# Wide ones have their own row, narrow ones are two to a row
if attr.type.is_wide:
if index % 2: # Row has a single narrow, skip the empty space.
index += 1
attr_frame.grid(
row=index // 2,
column=0, columnspan=3,
sticky='w',
)
index += 2
else:
if index % 2: # Right.
ttk.Separator(orient='vertical').grid(row=index // 2, column=1, sticky='NS')
match col_type:
case 'wide':
attr_frame.grid(
row=index // 2,
column=2,
sticky='E',
row=row,
column=0, columnspan=3,
sticky='w',
)
else:
case 'left':
attr_frame.grid(
row=index // 2,
row=row,
column=0,
sticky='W',
sticky='w',
)
index += 1
case 'right':
ttk.Separator(attrs_frame, orient='vertical').grid(row=row, column=1, sticky='NS')
attr_frame.grid(
row=row,
column=2,
sticky='e',
)
case _:
assert_never(col_type)

self.set_disp()
self.wid_canvas.bind("<Configure>", self.flow_items)
Expand Down Expand Up @@ -532,6 +528,21 @@ def _ui_sugg_place(self, label: SuggLabel, button: ttk.Button, x: int, y: int) -
label.place(x=x, y=y)
label['width'] = button.winfo_width()

@override
def _ui_attr_set_text(self, attr: AttrDef, text: TransToken, /) -> None:
"""Set the value of a text-style attribute widget."""
set_text(self.attr_labels[attr], text)

@override
def _ui_attr_set_image(self, attr: AttrDef, image: img.Handle, /) -> None:
"""Set the image for an image-style attribute widget."""
TK_IMG.apply(self.attr_labels[attr], image)

@override
def _ui_attr_set_tooltip(self, attr: AttrDef, tooltip: TransToken, /) -> None:
"""Set the hover tooltip. This only applies to image-style widgets."""
set_tooltip(self.attr_labels[attr], tooltip)

@override
def _ui_props_set_author(self, author: TransToken) -> None:
"""Set the author text for the selected item."""
Expand Down

0 comments on commit 070968c

Please sign in to comment.