Skip to content

Commit

Permalink
Fix #2082: Show a warning if right-clicking on invalid items
Browse files Browse the repository at this point in the history
  • Loading branch information
TeamSpen210 committed Oct 2, 2024
1 parent 32d30eb commit a5fde93
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 17 deletions.
57 changes: 44 additions & 13 deletions src/app/contextWin.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ class SPR(Enum):
'This fizzler has an output. Due to an editor bug, this cannot be used directly. Instead '
'the Fizzler Output Relay item should be placed on top of this fizzler.'
)
TRANS_URL_FAIL = TransToken.ui(
'Failed to open a web browser. Do you wish for the URL '
'to be copied to the clipboard instead?'
)
TRANS_TOOL_FIZZOUT_TIMED = TransToken.ui(
'This fizzler has a timed output. Due to an editor bug, this cannot be used directly. Instead '
'the Fizzler Output Relay item should be placed on top of this fizzler.'
Expand All @@ -120,6 +124,11 @@ class SPR(Enum):
'limits this to 2048 in total. This provides a guide to how many of '
'these items can be placed in a map at once.'
)
TRANS_MISSING_ITEM = TransToken.ui(
'The item <{id}> is missing from package definitions. Check for missing packages. '
'Alternatively, this item may have been replaced or merged into another.\n\n'
'Export is not possible while this is present on the palette.'
)


def pos_for_item(item: Item, ind: int) -> int | None:
Expand Down Expand Up @@ -175,6 +184,8 @@ class ContextWinBase:
current_style: AsyncValue[packages.PakRef[packages.Style]]
# If set, the item properties window is open and suppressing us.
props_open: bool
# If set, a special warning is visible for items that are not defined.
missing_item_visible: bool

moreinfo_url: AsyncValue[str | None]
moreinfo_trigger: EdgeTrigger[str]
Expand All @@ -193,6 +204,7 @@ def __init__(
self.picker = item_picker
self.current_style = current_style
self.props_open = False
self.missing_item_visible = False
self.packset = packages.PackagesSet.blank()

# The current URL in the more-info button, if available.
Expand Down Expand Up @@ -404,12 +416,16 @@ def load_item_data(self) -> None:
blurb = ('Output force-enabled!\n' + blurb).strip()
self.ui_set_sprite_tool(SPR.OUTPUT, TransToken.untranslated(blurb))

def hide_context(self, e: object = None) -> None:
def hide_context(self, _: object = None, /) -> None:
"""Hide the properties window, if it's open."""
if self.is_visible:
self.ui_hide_window()
sound.fx('contract')
self.selected = self.selected_slot = self.selected_pal_pos = None
if self.missing_item_visible:
self.ui_hide_missing_win()
sound.fx('contract')
self.missing_item_visible = False

def show_prop(
self,
Expand All @@ -424,25 +440,35 @@ def show_prop(
it stays on top of the selected subitem.
- If from the palette, pal_pos is the position.
"""
self.ui_hide_missing_win()
if warp_cursor and self.is_visible:
offset = self.ui_get_cursor_offset()
else:
offset = None
self.selected = slot.contents
if self.selected is None:
selected = slot.contents
if selected is None:
LOGGER.warning('Selected empty slot?')
self.hide_context()
return

x, y = slot.get_coords()

# Check to see if it's actually a valid item too.
if self.selected.item.resolve(self.packset) is None:
LOGGER.info('Item not defined, nothing to show.')
if selected.item.resolve(self.packset) is None:
LOGGER.info('Item {} not defined!', selected.item)
self.hide_context()
self.missing_item_visible = True
self.ui_show_missing_win(
x, y,
TRANS_MISSING_ITEM.format(id=selected.item),
)
return

self.selected = selected
self.selected_slot = slot
self.selected_pal_pos = pal_pos

x, y = slot.get_coords()
sound.fx('expand')
self.ui_show_window(x, y)
self.adjust_position()

Expand All @@ -460,10 +486,7 @@ async def _moreinfo_task(self) -> None:
except webbrowser.Error:
if await self.dialog.ask_yes_no(
title=TransToken.ui("BEE2 - Error"),
message=TransToken.ui(
'Failed to open a web browser. Do you wish for the URL '
'to be copied to the clipboard instead?'
),
message=TRANS_URL_FAIL,
icon=self.dialog.ERROR,
detail=f'"{url}"',
):
Expand All @@ -479,7 +502,7 @@ def set_sprite(self, pos: SPR, sprite: str) -> None:
self.ui_set_sprite_img(pos, img.Handle.sprite('icons/' + sprite, 32, 32))
self.ui_set_sprite_tool(pos, SPRITE_TOOL[sprite])

def sub_sel(self, pos: int, e: object = None) -> None:
def sub_sel(self, pos: int, _: object = None, /) -> None:
"""Change the currently-selected sub-item."""
if self.selected is None or self.selected_slot is None:
return
Expand All @@ -495,7 +518,7 @@ def sub_sel(self, pos: int, e: object = None) -> None:
# Redisplay the window to refresh data and move it to match
self.show_prop(self.selected_slot, self.selected_pal_pos, warp_cursor=True)

def sub_open(self, pos: int, e: object = None) -> None:
def sub_open(self, pos: int, _: object = None, /) -> None:
"""Move the context window to apply to the given item."""
assert self.selected is not None
item = self.selected.item.resolve(packages.get_loaded_packages())
Expand All @@ -510,7 +533,7 @@ def sub_open(self, pos: int, e: object = None) -> None:
if slot is not None:
self.show_prop(slot, pal_pos)

def adjust_position(self, e: object = None) -> None:
def adjust_position(self, _: object = None, /) -> None:
"""Move the properties window onto the selected item.
We call this constantly, so the property window will not go outside
Expand Down Expand Up @@ -573,6 +596,14 @@ def ui_show_window(self, x: int, y: int) -> None:
"""Show the window, at the specified position."""
raise NotImplementedError

def ui_show_missing_win(self, x: int, y: int, text: TransToken) -> None:
"""Show an error window identifying missing items."""
raise NotImplementedError

def ui_hide_missing_win(self) -> None:
"""Hide the missing-item window."""
raise NotImplementedError

def ui_get_cursor_offset(self) -> tuple[int, int]:
"""Fetch the offset of the cursor relative to the window, for restoring when it moves."""
raise NotImplementedError
Expand Down
3 changes: 1 addition & 2 deletions src/app/item_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Final

from abc import ABC, abstractmethod
from collections.abc import Callable, Mapping
from collections.abc import Awaitable, Callable, Mapping
from contextlib import aclosing
import random

Expand Down Expand Up @@ -203,7 +203,6 @@ async def open_contextwin_task(
while True:
slot = await self.drag_man.on_config.wait()
if slot.contents is not None:
sound.fx('expand')
open_func(slot, self.slots_pal.get(slot))

def _drag_info(self, ref: SubItemRef) -> DragInfo:
Expand Down
37 changes: 35 additions & 2 deletions src/ui_tk/context_win.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,20 @@ def __init__(
# When the main window moves, move the context window also.
TK_ROOT.bind("<Configure>", self.adjust_position, add='+')

self.missing_item_win = tk.Toplevel(TK_ROOT, name='contextWin_missing')
self.missing_item_win.overrideredirect(True)
self.missing_item_win.resizable(False, False)
self.missing_item_win.transient(master=TK_ROOT)
if utils.LINUX:
self.missing_item_win.wm_attributes('-type', 'popup_menu')
self.missing_item_win.withdraw() # starts hidden

missing_win_frame = ttk.Frame(self.missing_item_win, relief="raised", borderwidth="4")
missing_win_frame.grid()
self.wid_missing_lbl = ttk.Label(missing_win_frame)
self.wid_missing_lbl.grid()
self.wid_missing_lbl['wraplength'] = 300

def _evt_moreinfo_clicked(self) -> None:
"""Handle the more-info button being clicked."""
url = self.moreinfo_url.value
Expand Down Expand Up @@ -286,10 +300,29 @@ def ui_hide_window(self) -> None:
@override
def ui_show_window(self, x: int, y: int) -> None:
"""Show the window."""
loc_x, loc_y = tk_tools.adjust_inside_screen(x=x, y=y, win=self.window)
x, y = tk_tools.adjust_inside_screen(x, y, self.window)
self.window.deiconify()
self.window.lift()
self.window.geometry(f'+{loc_x!s}+{loc_y!s}')
self.window.geometry(f'+{x!s}+{y!s}')

@override
def ui_show_missing_win(self, x: int, y: int, label: TransToken) -> None:
"""Show the missing-item window."""
set_text(self.wid_missing_lbl, label)
self.missing_item_win.deiconify()
self.missing_item_win.lift()
self.missing_item_win.update_idletasks()
self.missing_item_win.bell()

x += (IMG_ALPHA.width - self.missing_item_win.winfo_width()) // 2
y += IMG_ALPHA.height // 4
x, y = tk_tools.adjust_inside_screen(x, y, self.missing_item_win)
self.missing_item_win.geometry(f'+{x!s}+{y!s}')

@override
def ui_hide_missing_win(self) -> None:
"""Hide the missing-item window."""
self.missing_item_win.withdraw()

@override
def ui_get_cursor_offset(self) -> tuple[int, int]:
Expand Down

0 comments on commit a5fde93

Please sign in to comment.