Skip to content

Commit a1cfabe

Browse files
Fix invisible window issue and refactor window sizer (#1201)
* Refactor the window resizer class This was difficult to follow with the class spread out over multiple mixin functions. This rewrites it to be a class which subclasses the original class. * Recover windows outside of the display area If a window location was on a monitor which is no longer connected it would get moved there regardless making it difficult to recover. This code checks if the window header is visible and recovers it automatically if it is not. * Reformat
1 parent 775217d commit a1cfabe

File tree

1 file changed

+72
-59
lines changed

1 file changed

+72
-59
lines changed
Lines changed: 72 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
from __future__ import annotations
2-
from typing import Type, Tuple
2+
3+
from typing import Type, Tuple, TypedDict, Dict
34

45
import atexit
56

67
import wx
78

89
from amulet_map_editor import CONFIG
910

10-
PRE_EXISTING_CONFIG = CONFIG.get("window_preferences", {})
11+
12+
class WindowConfig(TypedDict):
13+
size: Tuple[int, int]
14+
position: Tuple[int, int]
15+
is_full_screen: bool
16+
17+
18+
PRE_EXISTING_CONFIG: Dict[str, WindowConfig] = CONFIG.get("window_preferences", {})
1119

1220

1321
def write_config():
@@ -17,67 +25,72 @@ def write_config():
1725
atexit.register(write_config)
1826

1927

20-
class ExtendedTLW(wx.TopLevelWindow):
21-
__resized: bool
22-
__size: Tuple[int, int]
23-
__position: Tuple[int, int]
28+
def preserve_ui_preferences(cls: Type[wx.TopLevelWindow]):
29+
assert issubclass(
30+
cls, wx.TopLevelWindow
31+
), "This takes a subclass of a top level window."
2432

33+
qualified_name = ".".join((cls.__module__, cls.__qualname__))
2534

26-
def on_idle(self: ExtendedTLW):
27-
qualified_name = ".".join((self.__module__, self.__class__.__name__))
35+
class TopLevelWindowWrapper(cls):
36+
__resized: bool
37+
__size: Tuple[int, int]
38+
__position: Tuple[int, int]
2839

29-
def wrapper(evt):
30-
if self.__resized:
40+
def __init__(self, *args, **kwargs):
41+
super().__init__(*args, **kwargs)
3142
self.__resized = False
32-
PRE_EXISTING_CONFIG[qualified_name] = {
33-
"size": self.__size,
34-
"position": self.__position,
35-
"is_full_screen": self.IsMaximized(),
36-
}
37-
evt.Skip()
38-
39-
return wrapper
40-
41-
42-
def on_resize(self: ExtendedTLW):
43-
def wrapper(evt):
44-
self.__resized = True
45-
if not self.IsMaximized():
46-
# only store the non-maximised state
4743
self.__position = self.GetPosition().Get()
4844
self.__size = self.GetSize().Get()
49-
evt.Skip()
50-
51-
return wrapper
5245

53-
54-
def preserve_ui_preferences(clazz: Type[wx.TopLevelWindow]):
55-
assert issubclass(
56-
clazz, wx.TopLevelWindow
57-
), "This takes a subclass of a top level window."
58-
original_init = clazz.__init__
59-
qualified_name = ".".join((clazz.__module__, clazz.__name__))
60-
61-
def __init__(self: wx.TopLevelWindow, *args, **kwargs):
62-
original_init(self, *args, **kwargs)
63-
self.__resized = False
64-
self.__position = self.GetPosition().Get()
65-
self.__size = self.GetSize().Get()
66-
self: ExtendedTLW
67-
68-
if qualified_name in PRE_EXISTING_CONFIG:
69-
window_config = PRE_EXISTING_CONFIG[qualified_name]
70-
self.SetPosition(window_config["position"])
71-
self.SetSize(window_config["size"])
72-
self.Maximize(window_config.get("is_full_screen", False))
73-
else:
74-
self.Maximize()
75-
self.Layout()
76-
self.Refresh()
77-
78-
self.Bind(wx.EVT_MOVE, on_resize(self))
79-
self.Bind(wx.EVT_SIZE, on_resize(self))
80-
self.Bind(wx.EVT_IDLE, on_idle(self))
81-
82-
clazz.__init__ = __init__
83-
return clazz
46+
if qualified_name in PRE_EXISTING_CONFIG:
47+
window_config = PRE_EXISTING_CONFIG[qualified_name]
48+
x, y = window_config["position"]
49+
dx, dy = window_config["size"]
50+
51+
# Check if the window header intersects on a connected display.
52+
display_count = wx.Display.GetCount()
53+
for i in range(display_count):
54+
display = wx.Display(i)
55+
geometry = display.GetGeometry()
56+
if geometry.Intersects(wx.Rect(x, y, dx, 10)):
57+
break
58+
else:
59+
if display_count:
60+
# If there is no display at that point move it to the first display.
61+
display = wx.Display(0)
62+
geometry = display.GetGeometry()
63+
x = geometry.x
64+
y = geometry.y
65+
66+
self.SetPosition(wx.Point(x, y))
67+
self.SetSize(wx.Size(dx, dy))
68+
self.Maximize(window_config.get("is_full_screen", False))
69+
else:
70+
self.Maximize()
71+
self.Layout()
72+
self.Refresh()
73+
74+
self.Bind(wx.EVT_MOVE, self.__on_resize)
75+
self.Bind(wx.EVT_SIZE, self.__on_resize)
76+
self.Bind(wx.EVT_IDLE, self.__on_idle)
77+
78+
def __on_idle(self, evt):
79+
if self.__resized:
80+
self.__resized = False
81+
PRE_EXISTING_CONFIG[qualified_name] = {
82+
"size": self.__size,
83+
"position": self.__position,
84+
"is_full_screen": self.IsMaximized(),
85+
}
86+
evt.Skip()
87+
88+
def __on_resize(self, evt):
89+
self.__resized = True
90+
if not self.IsMaximized():
91+
# only store the non-maximised state
92+
self.__position = self.GetPosition().Get()
93+
self.__size = self.GetSize().Get()
94+
evt.Skip()
95+
96+
return TopLevelWindowWrapper

0 commit comments

Comments
 (0)