Compare commits
2 Commits
362383faf6
...
27c0e55711
| Author | SHA1 | Date |
|---|---|---|
|
|
27c0e55711 | |
|
|
f467e0b2e5 |
|
|
@ -76,8 +76,15 @@ class ICRAApp(
|
|||
self.root.overrideredirect(True)
|
||||
screen_width = self.root.winfo_screenwidth()
|
||||
screen_height = self.root.winfo_screenheight()
|
||||
default_width = int(screen_width * 0.8)
|
||||
default_height = int(screen_height * 0.8)
|
||||
default_x = (screen_width - default_width) // 2
|
||||
default_y = (screen_height - default_height) // 4
|
||||
self._window_geometry = f"{default_width}x{default_height}+{default_x}+{default_y}"
|
||||
self._is_maximized = True
|
||||
self.root.geometry(f"{screen_width}x{screen_height}+0+0")
|
||||
self.root.configure(bg="#f2f2f7")
|
||||
self.root.bind("<Map>", lambda _e: self.root.overrideredirect(True))
|
||||
|
||||
|
||||
def start_app() -> None:
|
||||
|
|
|
|||
147
app/gui/ui.py
147
app/gui/ui.py
|
|
@ -422,45 +422,118 @@ class UIBuilderMixin:
|
|||
)
|
||||
title_label.pack(side=tk.LEFT, padx=6)
|
||||
|
||||
close_btn = tk.Button(
|
||||
title_bar,
|
||||
text="✕",
|
||||
command=self._close_app,
|
||||
bg=bar_bg,
|
||||
fg="#f5f5f5",
|
||||
activebackground="#ff3b30",
|
||||
activeforeground="#ffffff",
|
||||
borderwidth=0,
|
||||
highlightthickness=0,
|
||||
relief="flat",
|
||||
font=("Segoe UI", 10, "bold"),
|
||||
cursor="hand2",
|
||||
width=3,
|
||||
)
|
||||
close_btn.pack(side=tk.RIGHT, padx=8, pady=4)
|
||||
btn_kwargs = {
|
||||
"bg": bar_bg,
|
||||
"fg": "#f5f5f5",
|
||||
"activebackground": "#3a3a40",
|
||||
"activeforeground": "#ffffff",
|
||||
"borderwidth": 0,
|
||||
"highlightthickness": 0,
|
||||
"relief": "flat",
|
||||
"font": ("Segoe UI", 10, "bold"),
|
||||
"cursor": "hand2",
|
||||
"width": 3,
|
||||
}
|
||||
|
||||
close_btn = tk.Button(title_bar, text="✕", command=self._close_app, **btn_kwargs)
|
||||
close_btn.pack(side=tk.RIGHT, padx=6, pady=4)
|
||||
close_btn.bind("<Enter>", lambda _e: close_btn.configure(bg="#cf212f"))
|
||||
close_btn.bind("<Leave>", lambda _e: close_btn.configure(bg=bar_bg))
|
||||
|
||||
for widget in (title_bar, title_label):
|
||||
widget.bind("<ButtonPress-1>", self._start_window_drag)
|
||||
widget.bind("<B1-Motion>", self._perform_window_drag)
|
||||
|
||||
def _close_app(self) -> None:
|
||||
try:
|
||||
self.root.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _start_window_drag(self, event) -> None:
|
||||
self._drag_offset = (event.x_root - self.root.winfo_rootx(), event.y_root - self.root.winfo_rooty())
|
||||
|
||||
def _perform_window_drag(self, event) -> None:
|
||||
offset = getattr(self, "_drag_offset", None)
|
||||
if offset is None:
|
||||
return
|
||||
x = event.x_root - offset[0]
|
||||
y = event.y_root - offset[1]
|
||||
self.root.geometry(f"+{x}+{y}")
|
||||
|
||||
max_btn = tk.Button(title_bar, text="❐", command=self._toggle_maximize_window, **btn_kwargs)
|
||||
max_btn.pack(side=tk.RIGHT, padx=0, pady=4)
|
||||
max_btn.bind("<Enter>", lambda _e: max_btn.configure(bg="#2c2c32"))
|
||||
max_btn.bind("<Leave>", lambda _e: max_btn.configure(bg=bar_bg))
|
||||
self._max_button = max_btn
|
||||
|
||||
min_btn = tk.Button(title_bar, text="—", command=self._minimize_window, **btn_kwargs)
|
||||
min_btn.pack(side=tk.RIGHT, padx=0, pady=4)
|
||||
min_btn.bind("<Enter>", lambda _e: min_btn.configure(bg="#2c2c32"))
|
||||
min_btn.bind("<Leave>", lambda _e: min_btn.configure(bg=bar_bg))
|
||||
|
||||
for widget in (title_bar, title_label):
|
||||
widget.bind("<ButtonPress-1>", self._start_window_drag)
|
||||
widget.bind("<B1-Motion>", self._perform_window_drag)
|
||||
widget.bind("<Double-Button-1>", lambda _e: self._toggle_maximize_window())
|
||||
|
||||
self._update_maximize_button()
|
||||
|
||||
def _close_app(self) -> None:
|
||||
try:
|
||||
self.root.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _start_window_drag(self, event) -> None:
|
||||
if getattr(self, "_is_maximized", False):
|
||||
cursor_x, cursor_y = event.x_root, event.y_root
|
||||
self._toggle_maximize_window(force_state=False)
|
||||
self.root.update_idletasks()
|
||||
new_x = self.root.winfo_rootx()
|
||||
new_y = self.root.winfo_rooty()
|
||||
self._drag_offset = (cursor_x - new_x, cursor_y - new_y)
|
||||
return
|
||||
self._drag_offset = (event.x_root - self.root.winfo_rootx(), event.y_root - self.root.winfo_rooty())
|
||||
|
||||
def _perform_window_drag(self, event) -> None:
|
||||
offset = getattr(self, "_drag_offset", None)
|
||||
if offset is None:
|
||||
return
|
||||
x = event.x_root - offset[0]
|
||||
y = event.y_root - offset[1]
|
||||
self.root.geometry(f"+{x}+{y}")
|
||||
if not getattr(self, "_is_maximized", False):
|
||||
self._remember_window_geometry()
|
||||
|
||||
def _remember_window_geometry(self) -> None:
|
||||
try:
|
||||
self._window_geometry = self.root.geometry()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _maximize_window(self) -> None:
|
||||
self._remember_window_geometry()
|
||||
screen_width = self.root.winfo_screenwidth()
|
||||
screen_height = self.root.winfo_screenheight()
|
||||
self.root.geometry(f"{screen_width}x{screen_height}+0+0")
|
||||
self._is_maximized = True
|
||||
self._update_maximize_button()
|
||||
|
||||
def _restore_window(self) -> None:
|
||||
geometry = getattr(self, "_window_geometry", None)
|
||||
if not geometry:
|
||||
screen_width = self.root.winfo_screenwidth()
|
||||
screen_height = self.root.winfo_screenheight()
|
||||
width = int(screen_width * 0.8)
|
||||
height = int(screen_height * 0.8)
|
||||
x = (screen_width - width) // 2
|
||||
y = (screen_height - height) // 4
|
||||
geometry = f"{width}x{height}+{x}+{y}"
|
||||
self.root.geometry(geometry)
|
||||
self._is_maximized = False
|
||||
self._update_maximize_button()
|
||||
|
||||
def _toggle_maximize_window(self, force_state: bool | None = None) -> None:
|
||||
desired = force_state if force_state is not None else not getattr(self, "_is_maximized", False)
|
||||
if desired:
|
||||
self._maximize_window()
|
||||
else:
|
||||
self._restore_window()
|
||||
|
||||
def _minimize_window(self) -> None:
|
||||
try:
|
||||
self.root.overrideredirect(False)
|
||||
self.root.iconify()
|
||||
self.root.after(50, lambda: self.root.overrideredirect(True))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _update_maximize_button(self) -> None:
|
||||
button = getattr(self, "_max_button", None)
|
||||
if button is None:
|
||||
return
|
||||
symbol = "❐" if getattr(self, "_is_maximized", False) else "□"
|
||||
button.configure(text=symbol)
|
||||
|
||||
def _maybe_focus_window(self, _event) -> None:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -194,7 +194,9 @@ class ImageProcessingMixin:
|
|||
return
|
||||
width, height = self.orig_img.size
|
||||
max_w, max_h = PREVIEW_MAX_SIZE
|
||||
scale = min(max_w / width, max_h / height, 1.0)
|
||||
scale = min(max_w / width, max_h / height)
|
||||
if scale <= 0:
|
||||
scale = 1.0
|
||||
size = (max(1, int(width * scale)), max(1, int(height * scale)))
|
||||
self.preview_img = self.orig_img.resize(size, Image.LANCZOS)
|
||||
self.preview_tk = ImageTk.PhotoImage(self.preview_img)
|
||||
|
|
|
|||
Loading…
Reference in New Issue