GUI-Politur: Hover-Effekte, edlere Karten, verfeinerte Farbpaletten, dünnere Scrollbar, aufgeräumte Sidebar-Stats
Build Windows / build (push) Has been cancelled
Build Windows / build (push) Has been cancelled
- Buttons/Nav/Filter-Chips mit Hover-Farbwechsel und aktiver Akzent-Kante - Server-Karten mit Status rechts, Verbinden-Hinweis und Akzent-Rahmen bei Hover - Shell-Karten mit '»'-Badge - Verfeinerte Farbpaletten (kontrastreicher) + eigener Hover-Akzent pro Theme - Sidebar-Stats mit Version, farbigem Terminal-Status und Server-Zähler - Empty-States mit Symbol, Scrollbar schlanker (6px, Akzent-Scrollbalken) - Version 1.2.0
This commit is contained in:
@@ -7,6 +7,7 @@ Läuft nativ auf **Linux** und **Windows**. Für Windows liegt ein fertiger Inst
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Desktop-UI ohne Browser
|
- Desktop-UI ohne Browser
|
||||||
|
- Aufpolierte Oberfläche mit Hover-Effekten, Akzent-Design und edlen Karten
|
||||||
- Serverliste mit Suche
|
- Serverliste mit Suche
|
||||||
- Server hinzufügen, bearbeiten, duplizieren und löschen
|
- Server hinzufügen, bearbeiten, duplizieren und löschen
|
||||||
- Gruppenfilter in der Sidebar
|
- Gruppenfilter in der Sidebar
|
||||||
|
|||||||
+164
-84
@@ -17,7 +17,7 @@ import yaml
|
|||||||
|
|
||||||
|
|
||||||
APP_TITLE = "PulseGate Desktop"
|
APP_TITLE = "PulseGate Desktop"
|
||||||
APP_VERSION = "1.1.0"
|
APP_VERSION = "1.2.0"
|
||||||
APP_URL = "https://git.pepe44.dev/Pepe44DEV/PulseGate-GUI"
|
APP_URL = "https://git.pepe44.dev/Pepe44DEV/PulseGate-GUI"
|
||||||
IS_WINDOWS = sys.platform == "win32"
|
IS_WINDOWS = sys.platform == "win32"
|
||||||
CONNECT_TIMEOUT = 2.5
|
CONNECT_TIMEOUT = 2.5
|
||||||
@@ -25,60 +25,64 @@ STATUS_OK = "#00e07a"
|
|||||||
|
|
||||||
THEMES = {
|
THEMES = {
|
||||||
"neon-green": {
|
"neon-green": {
|
||||||
"bg": "#07110d",
|
"bg": "#050d09",
|
||||||
"sidebar": "#050c09",
|
"sidebar": "#040b08",
|
||||||
"surface": "#0d1b15",
|
"surface": "#0c1a13",
|
||||||
"surface_2": "#13251e",
|
"surface_2": "#12241b",
|
||||||
"surface_3": "#172c24",
|
"surface_3": "#193127",
|
||||||
"field": "#09150f",
|
"field": "#08130e",
|
||||||
"line": "#1f4b38",
|
"line": "#1d4a37",
|
||||||
"text": "#d7ffe9",
|
"text": "#d9ffe9",
|
||||||
"muted": "#8aa99b",
|
"muted": "#86a998",
|
||||||
"accent": "#00ff99",
|
"accent": "#00ff99",
|
||||||
"accent_2": "#33ccff",
|
"accent_hi": "#5cffb9",
|
||||||
"danger": "#ff6666",
|
"accent_2": "#3ecfff",
|
||||||
|
"danger": "#ff7070",
|
||||||
},
|
},
|
||||||
"ocean-blue": {
|
"ocean-blue": {
|
||||||
"bg": "#071018",
|
"bg": "#061019",
|
||||||
"sidebar": "#040b12",
|
"sidebar": "#040b13",
|
||||||
"surface": "#0e1b26",
|
"surface": "#0d1d29",
|
||||||
"surface_2": "#142638",
|
"surface_2": "#13293a",
|
||||||
"surface_3": "#1a324a",
|
"surface_3": "#1a364c",
|
||||||
"field": "#091420",
|
"field": "#081420",
|
||||||
"line": "#1e4a6e",
|
"line": "#1c4a6e",
|
||||||
"text": "#d7ecff",
|
"text": "#d8edff",
|
||||||
"muted": "#8aa7c0",
|
"muted": "#86a7c2",
|
||||||
"accent": "#3ddcff",
|
"accent": "#3ddcff",
|
||||||
"accent_2": "#5a8cff",
|
"accent_hi": "#8fecff",
|
||||||
"danger": "#ff6a6a",
|
"accent_2": "#6f9bff",
|
||||||
|
"danger": "#ff7a7a",
|
||||||
},
|
},
|
||||||
"sunset-orange": {
|
"sunset-orange": {
|
||||||
"bg": "#120d07",
|
"bg": "#130d06",
|
||||||
"sidebar": "#0b0804",
|
"sidebar": "#0e0904",
|
||||||
"surface": "#221711",
|
"surface": "#241911",
|
||||||
"surface_2": "#2f2215",
|
"surface_2": "#312419",
|
||||||
"surface_3": "#3b2d1d",
|
"surface_3": "#3f2f20",
|
||||||
"field": "#17100a",
|
"field": "#181009",
|
||||||
"line": "#6e4a1e",
|
"line": "#6c4a1d",
|
||||||
"text": "#ffe9d7",
|
"text": "#ffead6",
|
||||||
"muted": "#c0a98a",
|
"muted": "#c3ab8b",
|
||||||
"accent": "#ff9d3d",
|
"accent": "#ff9d3d",
|
||||||
"accent_2": "#ff6a5a",
|
"accent_hi": "#ffbd7c",
|
||||||
"danger": "#ff6a6a",
|
"accent_2": "#ff7a5c",
|
||||||
|
"danger": "#ff7a7a",
|
||||||
},
|
},
|
||||||
"cyber-purple": {
|
"cyber-purple": {
|
||||||
"bg": "#0f0711",
|
"bg": "#0f0712",
|
||||||
"sidebar": "#09050b",
|
"sidebar": "#0a050c",
|
||||||
"surface": "#1d0e22",
|
"surface": "#1e0f24",
|
||||||
"surface_2": "#2a1133",
|
"surface_2": "#2b1534",
|
||||||
"surface_3": "#371a44",
|
"surface_3": "#3a1d46",
|
||||||
"field": "#140918",
|
"field": "#150a19",
|
||||||
"line": "#5a1f6e",
|
"line": "#5c2172",
|
||||||
"text": "#f0d7ff",
|
"text": "#f2d9ff",
|
||||||
"muted": "#b38ac0",
|
"muted": "#b590c2",
|
||||||
"accent": "#c95aff",
|
"accent": "#c95aff",
|
||||||
|
"accent_hi": "#e0a0ff",
|
||||||
"accent_2": "#6affcc",
|
"accent_2": "#6affcc",
|
||||||
"danger": "#ff6a6a",
|
"danger": "#ff7a7a",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,10 +362,15 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
|
|
||||||
stats = self._card(sidebar, bg=COLORS["surface"], padx=14, pady=12)
|
stats = self._card(sidebar, bg=COLORS["surface"], padx=14, pady=12)
|
||||||
stats.pack(fill="x", padx=22, pady=(28, 10))
|
stats.pack(fill="x", padx=22, pady=(28, 10))
|
||||||
self.server_count_label = self._label(stats, "", bg=COLORS["surface"], fg=COLORS["text"], font=FONT_CARD)
|
head = tk.Frame(stats, bg=COLORS["surface"])
|
||||||
self.server_count_label.pack(anchor="w")
|
head.pack(fill="x")
|
||||||
|
self._label(head, "PulseGate", bg=COLORS["surface"], fg=COLORS["text"], font=FONT_CARD).pack(side="left")
|
||||||
|
self._label(head, "v" + APP_VERSION, bg=COLORS["surface"], fg=COLORS["muted"], font=FONT_SMALL).pack(side="right", anchor="e", pady=(3, 0))
|
||||||
terminal_text = "Terminal bereit" if self.terminal else "Kein Terminal gefunden"
|
terminal_text = "Terminal bereit" if self.terminal else "Kein Terminal gefunden"
|
||||||
self._label(stats, terminal_text, bg=COLORS["surface"], fg=COLORS["accent_2"], font=FONT_SMALL).pack(anchor="w", pady=(7, 0))
|
terminal_fg = STATUS_OK if self.terminal else COLORS["danger"]
|
||||||
|
self._label(stats, f"● {terminal_text}", bg=COLORS["surface"], fg=terminal_fg, font=FONT_SMALL).pack(anchor="w", pady=(8, 0))
|
||||||
|
self.server_count_label = self._label(stats, "0 Server", bg=COLORS["surface"], fg=COLORS["accent"], font=(UI_FONT[0], 10, "bold"))
|
||||||
|
self.server_count_label.pack(anchor="w", pady=(7, 0))
|
||||||
self._label(stats, str(self.path), bg=COLORS["surface"], fg=COLORS["muted"], font=FONT_SMALL, wraplength=210).pack(anchor="w", pady=(8, 0))
|
self._label(stats, str(self.path), bg=COLORS["surface"], fg=COLORS["muted"], font=FONT_SMALL, wraplength=210).pack(anchor="w", pady=(8, 0))
|
||||||
|
|
||||||
self._button(sidebar, "Über PulseGate", self._about, variant="ghost").pack(fill="x", padx=22, side="bottom", pady=(0, 16))
|
self._button(sidebar, "Über PulseGate", self._about, variant="ghost").pack(fill="x", padx=22, side="bottom", pady=(0, 16))
|
||||||
@@ -399,7 +408,17 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
left.pack_propagate(False)
|
left.pack_propagate(False)
|
||||||
|
|
||||||
self.server_canvas = tk.Canvas(left, bg=COLORS["bg"], highlightthickness=0, bd=0)
|
self.server_canvas = tk.Canvas(left, bg=COLORS["bg"], highlightthickness=0, bd=0)
|
||||||
scrollbar = tk.Scrollbar(left, orient="vertical", command=self.server_canvas.yview, bg=COLORS["bg"], troughcolor=COLORS["bg"])
|
scrollbar = tk.Scrollbar(
|
||||||
|
left,
|
||||||
|
orient="vertical",
|
||||||
|
command=self.server_canvas.yview,
|
||||||
|
bg=COLORS["surface_3"],
|
||||||
|
activebackground=COLORS["accent"],
|
||||||
|
troughcolor=COLORS["bg"],
|
||||||
|
relief="flat",
|
||||||
|
bd=0,
|
||||||
|
width=6,
|
||||||
|
)
|
||||||
self.server_cards = tk.Frame(self.server_canvas, bg=COLORS["bg"])
|
self.server_cards = tk.Frame(self.server_canvas, bg=COLORS["bg"])
|
||||||
self.server_cards.bind("<Configure>", lambda _event: self.server_canvas.configure(scrollregion=self.server_canvas.bbox("all")))
|
self.server_cards.bind("<Configure>", lambda _event: self.server_canvas.configure(scrollregion=self.server_canvas.bbox("all")))
|
||||||
self.server_canvas.create_window((0, 0), window=self.server_cards, anchor="nw", width=382)
|
self.server_canvas.create_window((0, 0), window=self.server_cards, anchor="nw", width=382)
|
||||||
@@ -567,16 +586,27 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
text=group,
|
text=group,
|
||||||
bg=COLORS["accent"] if selected else COLORS["surface_2"],
|
bg=COLORS["accent"] if selected else COLORS["surface_2"],
|
||||||
fg="#001a10" if selected else COLORS["text"],
|
fg="#001a10" if selected else COLORS["text"],
|
||||||
font=FONT_SMALL,
|
font=(UI_FONT[0], 9, "bold"),
|
||||||
padx=9,
|
padx=10,
|
||||||
pady=4,
|
pady=5,
|
||||||
cursor="hand2",
|
cursor="hand2",
|
||||||
highlightthickness=1,
|
highlightthickness=1,
|
||||||
highlightbackground=COLORS["line"],
|
highlightbackground=COLORS["line"] if not selected else COLORS["accent_hi"],
|
||||||
)
|
)
|
||||||
chip.pack(side="left", padx=(0, 6))
|
chip.pack(side="left", padx=(0, 6))
|
||||||
chip.bind("<Button-1>", lambda _event, value=group: self._set_group_filter(value))
|
chip.bind("<Button-1>", lambda _event, value=group: self._set_group_filter(value))
|
||||||
|
|
||||||
|
def _enter(_event, label=chip, is_selected=selected) -> None:
|
||||||
|
if not is_selected:
|
||||||
|
label.configure(bg=COLORS["surface_3"], highlightbackground=COLORS["accent_hi"])
|
||||||
|
|
||||||
|
def _leave(_event, label=chip, is_selected=selected) -> None:
|
||||||
|
if not is_selected:
|
||||||
|
label.configure(bg=COLORS["surface_2"], highlightbackground=COLORS["line"])
|
||||||
|
|
||||||
|
chip.bind("<Enter>", _enter)
|
||||||
|
chip.bind("<Leave>", _leave)
|
||||||
|
|
||||||
def _set_group_filter(self, group: str) -> None:
|
def _set_group_filter(self, group: str) -> None:
|
||||||
self.group_filter = group
|
self.group_filter = group
|
||||||
self._refresh_group_chips()
|
self._refresh_group_chips()
|
||||||
@@ -611,43 +641,61 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
selected = index == self.selected_index
|
selected = index == self.selected_index
|
||||||
bg = COLORS["surface_2"] if selected else COLORS["surface"]
|
bg = COLORS["surface_2"] if selected else COLORS["surface"]
|
||||||
border = COLORS["accent"] if selected else COLORS["line"]
|
border = COLORS["accent"] if selected else COLORS["line"]
|
||||||
|
|
||||||
card = tk.Frame(parent, bg=border, bd=0)
|
card = tk.Frame(parent, bg=border, bd=0)
|
||||||
inner = tk.Frame(card, bg=bg, padx=14, pady=12)
|
inner = tk.Frame(card, bg=bg, padx=14, pady=12)
|
||||||
inner.pack(fill="both", expand=True, padx=1, pady=1)
|
inner.pack(fill="both", expand=True, padx=1, pady=1)
|
||||||
|
|
||||||
target = f"{server.get('user')}@{server.get('host')}:{server.get('port') or 22}"
|
head = tk.Frame(inner, bg=bg)
|
||||||
name = server.get("name") or "Unbenannt"
|
head.pack(fill="x", pady=(0, 6))
|
||||||
group = server.get("group") or "Keine Gruppe"
|
|
||||||
|
|
||||||
self._label(inner, name, bg=bg, fg=COLORS["text"], font=FONT_CARD).pack(anchor="w")
|
name = server.get("name") or "Unbenannt"
|
||||||
self._label(inner, target, bg=bg, fg=COLORS["muted"], font=FONT_SMALL).pack(anchor="w", pady=(5, 8))
|
self._label(head, name, bg=bg, fg=COLORS["text"], font=FONT_CARD).pack(side="left")
|
||||||
|
|
||||||
|
status = self.reachability.get(index)
|
||||||
|
if status is None:
|
||||||
|
status_text, status_fg = "…", COLORS["muted"]
|
||||||
|
elif status:
|
||||||
|
status_text, status_fg = "● erreichbar", STATUS_OK
|
||||||
|
else:
|
||||||
|
status_text, status_fg = "● offline", COLORS["danger"]
|
||||||
|
status_label = self._label(head, status_text, bg=bg, fg=status_fg, font=FONT_SMALL)
|
||||||
|
status_label.pack(side="right", anchor="e", pady=(2, 0))
|
||||||
|
self.reach_labels[index] = status_label
|
||||||
|
|
||||||
|
target = f"{server.get('user')}@{server.get('host')}:{server.get('port') or 22}"
|
||||||
|
self._label(inner, target, bg=bg, fg=COLORS["muted"], font=UI_FONT_MONO).pack(anchor="w", pady=(0, 10))
|
||||||
|
|
||||||
chips = tk.Frame(inner, bg=bg)
|
chips = tk.Frame(inner, bg=bg)
|
||||||
chips.pack(fill="x")
|
chips.pack(fill="x")
|
||||||
self._chip(chips, group, COLORS["accent_2"], bg=bg).pack(side="left")
|
self._chip(chips, server.get("group") or "Keine Gruppe", COLORS["accent_2"], bg=bg).pack(side="left")
|
||||||
self._chip(chips, server.get("auth") or "key", COLORS["accent"], bg=bg).pack(side="left", padx=(6, 0))
|
self._chip(chips, server.get("auth") or "key", COLORS["accent"], bg=bg).pack(side="left", padx=(6, 0))
|
||||||
if server.get("kitty_fix"):
|
if server.get("kitty_fix"):
|
||||||
self._chip(chips, "kitty", COLORS["muted"], bg=bg).pack(side="left", padx=(6, 0))
|
self._chip(chips, "kitty", COLORS["muted"], bg=bg).pack(side="left", padx=(6, 0))
|
||||||
|
|
||||||
status = self.reachability.get(index)
|
connect_hint = tk.Label(inner, text="Verbinden ›", bg=bg, fg=COLORS["accent_hi"], font=FONT_SMALL)
|
||||||
if status is None:
|
connect_hint.place(relx=1.0, rely=0.0, anchor="ne", x=-14, y=6)
|
||||||
status_text, status_fg = "● prüfe ...", COLORS["muted"]
|
|
||||||
elif status:
|
def _card_enter(_event) -> None:
|
||||||
status_text, status_fg = "● erreichbar", STATUS_OK
|
card.configure(bg=COLORS["accent_hi"])
|
||||||
else:
|
connect_hint.lift()
|
||||||
status_text, status_fg = "● nicht erreichbar", COLORS["danger"]
|
|
||||||
status_label = self._label(inner, status_text, bg=bg, fg=status_fg, font=FONT_SMALL)
|
def _card_leave(_event) -> None:
|
||||||
status_label.pack(anchor="w", pady=(8, 0))
|
card.configure(bg=border)
|
||||||
self.reach_labels[index] = status_label
|
connect_hint.lower()
|
||||||
|
|
||||||
for widget in (card, inner):
|
for widget in (card, inner):
|
||||||
widget.bind("<Button-1>", lambda _event, value=index: self._select_server(value))
|
widget.bind("<Button-1>", lambda _event, value=index: self._select_server(value))
|
||||||
widget.bind("<Double-Button-1>", lambda _event, value=index: self._connect_at(value))
|
widget.bind("<Double-Button-1>", lambda _event, value=index: self._connect_at(value))
|
||||||
widget.bind("<Button-3>", lambda event, value=index: self._server_menu(event, value))
|
widget.bind("<Button-3>", lambda event, value=index: self._server_menu(event, value))
|
||||||
|
widget.bind("<Enter>", _card_enter)
|
||||||
|
widget.bind("<Leave>", _card_leave)
|
||||||
for child in inner.winfo_children():
|
for child in inner.winfo_children():
|
||||||
child.bind("<Button-1>", lambda _event, value=index: self._select_server(value))
|
child.bind("<Button-1>", lambda _event, value=index: self._select_server(value))
|
||||||
child.bind("<Double-Button-1>", lambda _event, value=index: self._connect_at(value))
|
child.bind("<Double-Button-1>", lambda _event, value=index: self._connect_at(value))
|
||||||
child.bind("<Button-3>", lambda event, value=index: self._server_menu(event, value))
|
child.bind("<Button-3>", lambda event, value=index: self._server_menu(event, value))
|
||||||
|
child.bind("<Enter>", _card_enter)
|
||||||
|
child.bind("<Leave>", _card_leave)
|
||||||
|
|
||||||
return card
|
return card
|
||||||
|
|
||||||
@@ -698,8 +746,13 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
for command in commands:
|
for command in commands:
|
||||||
card = self._card(self.commands_host, bg=COLORS["surface"], padx=16, pady=14)
|
card = self._card(self.commands_host, bg=COLORS["surface"], padx=16, pady=14)
|
||||||
card.pack(fill="x", pady=(0, 10))
|
card.pack(fill="x", pady=(0, 10))
|
||||||
self._label(card, command.get("name") or "Command", bg=COLORS["surface"], fg=COLORS["text"], font=FONT_CARD).pack(anchor="w")
|
|
||||||
self._label(card, command.get("command") or "", bg=COLORS["surface"], fg=COLORS["accent"], font=UI_FONT_MONO).pack(anchor="w", pady=(7, 0))
|
head = tk.Frame(card, bg=COLORS["surface"])
|
||||||
|
head.pack(fill="x")
|
||||||
|
self._label(head, "»", bg=COLORS["surface"], fg=COLORS["accent"], font=(UI_FONT[0], 14, "bold")).pack(side="left", padx=(0, 8))
|
||||||
|
self._label(head, command.get("name") or "Command", bg=COLORS["surface"], fg=COLORS["text"], font=FONT_CARD).pack(side="left")
|
||||||
|
|
||||||
|
self._label(card, command.get("command") or "", bg=COLORS["surface"], fg=COLORS["accent"], font=UI_FONT_MONO).pack(anchor="w", pady=(9, 0))
|
||||||
|
|
||||||
actions = tk.Frame(card, bg=COLORS["surface"])
|
actions = tk.Frame(card, bg=COLORS["surface"])
|
||||||
actions.pack(fill="x", pady=(12, 0))
|
actions.pack(fill="x", pady=(12, 0))
|
||||||
@@ -1009,36 +1062,44 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
return widget
|
return widget
|
||||||
|
|
||||||
def _button(self, parent: tk.Widget, text: str, command, *, variant: str = "primary") -> tk.Button:
|
def _button(self, parent: tk.Widget, text: str, command, *, variant: str = "primary") -> tk.Button:
|
||||||
|
if variant == "primary":
|
||||||
bg = COLORS["accent"]
|
bg = COLORS["accent"]
|
||||||
fg = "#001a10"
|
fg = "#001a10"
|
||||||
active_bg = COLORS["accent"]
|
hover_bg = COLORS["accent_hi"]
|
||||||
if variant == "ghost":
|
hover_fg = "#001a10"
|
||||||
|
elif variant == "ghost":
|
||||||
bg = COLORS["surface_2"]
|
bg = COLORS["surface_2"]
|
||||||
fg = COLORS["text"]
|
fg = COLORS["text"]
|
||||||
active_bg = COLORS["surface_3"]
|
hover_bg = COLORS["surface_3"]
|
||||||
elif variant == "danger":
|
hover_fg = COLORS["text"]
|
||||||
bg = "#351414"
|
else:
|
||||||
|
bg = "#3a1717"
|
||||||
fg = COLORS["danger"]
|
fg = COLORS["danger"]
|
||||||
active_bg = "#4a1d1d"
|
hover_bg = "#522020"
|
||||||
|
hover_fg = "#ffd0d0"
|
||||||
|
|
||||||
return tk.Button(
|
button = tk.Button(
|
||||||
parent,
|
parent,
|
||||||
text=text,
|
text=text,
|
||||||
command=command,
|
command=command,
|
||||||
bg=bg,
|
bg=bg,
|
||||||
fg=fg,
|
fg=fg,
|
||||||
activebackground=active_bg,
|
activebackground=hover_bg,
|
||||||
activeforeground=fg,
|
activeforeground=hover_fg,
|
||||||
relief="flat",
|
relief="flat",
|
||||||
bd=0,
|
bd=0,
|
||||||
padx=14,
|
padx=14,
|
||||||
pady=9,
|
pady=9,
|
||||||
font=(UI_FONT[0], 10, "bold"),
|
font=(UI_FONT[0], 10, "bold"),
|
||||||
cursor="hand2",
|
cursor="hand2",
|
||||||
|
highlightthickness=0,
|
||||||
)
|
)
|
||||||
|
button.bind("<Enter>", lambda _event: button.configure(bg=hover_bg, fg=hover_fg))
|
||||||
|
button.bind("<Leave>", lambda _event: button.configure(bg=bg, fg=fg))
|
||||||
|
return button
|
||||||
|
|
||||||
def _nav_button(self, parent: tk.Widget, text: str, command) -> tk.Button:
|
def _nav_button(self, parent: tk.Widget, text: str, command) -> tk.Button:
|
||||||
return tk.Button(
|
button = tk.Button(
|
||||||
parent,
|
parent,
|
||||||
text=text,
|
text=text,
|
||||||
command=command,
|
command=command,
|
||||||
@@ -1057,6 +1118,24 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
highlightbackground=COLORS["sidebar"],
|
highlightbackground=COLORS["sidebar"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _enter(_event) -> None:
|
||||||
|
if not self._view_active(text):
|
||||||
|
button.configure(bg=COLORS["surface_2"])
|
||||||
|
|
||||||
|
def _leave(_event) -> None:
|
||||||
|
if self._view_active(text):
|
||||||
|
button.configure(bg=COLORS["surface_2"], highlightbackground=COLORS["line"])
|
||||||
|
else:
|
||||||
|
button.configure(bg=COLORS["sidebar"], highlightbackground=COLORS["sidebar"])
|
||||||
|
|
||||||
|
button.bind("<Enter>", _enter)
|
||||||
|
button.bind("<Leave>", _leave)
|
||||||
|
return button
|
||||||
|
|
||||||
|
def _view_active(self, name: str) -> bool:
|
||||||
|
mapping = {"Server": "servers", "Commands": "commands", "Settings": "settings"}
|
||||||
|
return mapping.get(name) == self.current_view
|
||||||
|
|
||||||
def _card(self, parent: tk.Widget, *, bg: str, padx: int, pady: int) -> tk.Frame:
|
def _card(self, parent: tk.Widget, *, bg: str, padx: int, pady: int) -> tk.Frame:
|
||||||
return tk.Frame(parent, bg=bg, padx=padx, pady=pady, highlightthickness=1, highlightbackground=COLORS["line"])
|
return tk.Frame(parent, bg=bg, padx=padx, pady=pady, highlightthickness=1, highlightbackground=COLORS["line"])
|
||||||
|
|
||||||
@@ -1066,16 +1145,17 @@ class PulseGateDesktop(tk.Tk):
|
|||||||
text=text,
|
text=text,
|
||||||
bg=bg or parent.cget("bg"),
|
bg=bg or parent.cget("bg"),
|
||||||
fg=fg,
|
fg=fg,
|
||||||
font=FONT_SMALL,
|
font=(UI_FONT[0], 9, "bold"),
|
||||||
padx=7,
|
padx=7,
|
||||||
pady=3,
|
pady=3,
|
||||||
highlightthickness=1,
|
highlightthickness=1,
|
||||||
highlightbackground=COLORS["line"],
|
highlightbackground=COLORS["surface_3"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def _empty_state(self, parent: tk.Widget, text: str) -> tk.Frame:
|
def _empty_state(self, parent: tk.Widget, text: str, symbol: str = "◦") -> tk.Frame:
|
||||||
card = self._card(parent, bg=COLORS["surface"], padx=16, pady=18)
|
card = self._card(parent, bg=COLORS["surface"], padx=16, pady=18)
|
||||||
self._label(card, text, bg=COLORS["surface"], fg=COLORS["muted"], font=FONT).pack(anchor="w")
|
self._label(card, symbol, bg=COLORS["surface"], fg=COLORS["accent"], font=(UI_FONT[0], 18, "bold")).pack(side="left", padx=(0, 12))
|
||||||
|
self._label(card, text, bg=COLORS["surface"], fg=COLORS["muted"], font=FONT).pack(side="left")
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
; Kompilieren: iscc installer\installer.iss (Inno Setup 6)
|
; Kompilieren: iscc installer\installer.iss (Inno Setup 6)
|
||||||
|
|
||||||
#define MyAppName "PulseGate Desktop"
|
#define MyAppName "PulseGate Desktop"
|
||||||
#define MyAppVersion "1.1.0"
|
#define MyAppVersion "1.2.0"
|
||||||
#define MyAppPublisher "Pepe44DEV"
|
#define MyAppPublisher "Pepe44DEV"
|
||||||
#define MyAppURL "https://git.pepe44.dev/Pepe44DEV/PulseGate-GUI"
|
#define MyAppURL "https://git.pepe44.dev/Pepe44DEV/PulseGate-GUI"
|
||||||
#define MyAppExeName "PulseGate.exe"
|
#define MyAppExeName "PulseGate.exe"
|
||||||
|
|||||||
Reference in New Issue
Block a user