update
This commit is contained in:
633
AzA march 2026/aza_office_workspace_ui.py
Normal file
633
AzA march 2026/aza_office_workspace_ui.py
Normal file
@@ -0,0 +1,633 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Verwaltungsfenster für Autotext und Textblöcke — ruhige Office-Hülle, ohne alte Hauptfenster-Optik."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
from typing import Dict
|
||||
|
||||
FF = "Segoe UI"
|
||||
|
||||
_AUTOTEXT_ATTR = "_aza_workspace_autotext_toplevel"
|
||||
_TB_WIN_DICT_ATTR = "_aza_workspace_tb_toplevels"
|
||||
|
||||
|
||||
def _palette_for_app(app) -> Dict[str, str]:
|
||||
sh = getattr(app, "_aza_office_v1", None)
|
||||
p = getattr(sh, "_palette", None) if sh is not None else None
|
||||
if isinstance(p, dict) and p:
|
||||
return dict(p)
|
||||
try:
|
||||
from aza_office_shell_v1 import PALETTE_LIGHT
|
||||
|
||||
return dict(PALETTE_LIGHT)
|
||||
except Exception:
|
||||
return {
|
||||
"BG": "#EAF2F7",
|
||||
"SURFACE": "#FFFFFF",
|
||||
"BORDER": "#D6E2EB",
|
||||
"TEXT": "#1A4D6D",
|
||||
"SUBTLE": "#5C7A8E",
|
||||
"ACCENT": "#5B8DB3",
|
||||
"TEXT_AREA_BG": "#FFFFFF",
|
||||
"TEXT_AREA_FG": "#1A4D6D",
|
||||
"ACCENT_SOFT": "#E2EEF6",
|
||||
"TEXT_STRONG": "#0F3850",
|
||||
}
|
||||
|
||||
|
||||
def _header_bar(parent, palette: Dict[str, str], title: str) -> tk.Frame:
|
||||
bar = tk.Frame(parent, bg=palette["ACCENT"], bd=0, highlightthickness=0)
|
||||
bar.pack(fill="x")
|
||||
tk.Label(
|
||||
bar, text=title, bg=palette["ACCENT"], fg="white",
|
||||
font=(FF, 10, "bold"), padx=12, pady=8, anchor="w",
|
||||
).pack(side="left")
|
||||
return bar
|
||||
|
||||
|
||||
def _register_if_any(app, win: tk.Toplevel) -> None:
|
||||
reg = getattr(app, "_register_window", None)
|
||||
if callable(reg):
|
||||
try:
|
||||
reg(win)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _lift_above_main(win: tk.Toplevel, app: tk.Misc) -> None:
|
||||
"""Hält den Dialog über dem Hauptfenster (Windows: transient + lift + topmost)."""
|
||||
try:
|
||||
win.transient(app)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
win.lift(app)
|
||||
win.focus_force()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
win.attributes("-topmost", True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def open_workspace_autotext_manager(app) -> None:
|
||||
"""Genau ein Autotext-Fenster; Kürzel wie bisher über basis14 / Persistenz."""
|
||||
from aza_ui_helpers import (
|
||||
center_window,
|
||||
load_toplevel_geometry,
|
||||
save_toplevel_geometry,
|
||||
)
|
||||
from aza_persistence import load_autotext, save_autotext
|
||||
from aza_workspace_sync import (
|
||||
prune_autotext_meta,
|
||||
schedule_workspace_cloud_push,
|
||||
touch_autotext_entry_meta,
|
||||
)
|
||||
|
||||
exist = getattr(app, _AUTOTEXT_ATTR, None)
|
||||
if exist is not None:
|
||||
try:
|
||||
if exist.winfo_exists():
|
||||
rf = getattr(exist, "_aza_reload_autotext_disk", None)
|
||||
if callable(rf):
|
||||
rf()
|
||||
exist.deiconify()
|
||||
_lift_above_main(exist, app)
|
||||
return
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
p = _palette_for_app(app)
|
||||
|
||||
root = tk.Toplevel(app)
|
||||
setattr(app, _AUTOTEXT_ATTR, root)
|
||||
root.title("Autotext")
|
||||
root.configure(bg=p["SURFACE"])
|
||||
root.minsize(560, 540)
|
||||
root.transient(app)
|
||||
_register_if_any(app, root)
|
||||
|
||||
geom_name = "workspace_autotext"
|
||||
saved = load_toplevel_geometry(geom_name)
|
||||
if saved:
|
||||
try:
|
||||
root.geometry(saved)
|
||||
except tk.TclError:
|
||||
root.geometry("720x640")
|
||||
center_window(root, 720, 640)
|
||||
else:
|
||||
root.geometry("720x640")
|
||||
center_window(root, 720, 640)
|
||||
|
||||
dik_state = {"active": False}
|
||||
|
||||
def _clear_ref_and_close() -> None:
|
||||
if dik_state["active"]:
|
||||
dik_state["active"] = False
|
||||
try:
|
||||
rec = getattr(app, "recorder", None)
|
||||
if rec is not None:
|
||||
try:
|
||||
rec.stop_and_save_wav()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if getattr(app, _AUTOTEXT_ATTR, None) is root:
|
||||
setattr(app, _AUTOTEXT_ATTR, None)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
save_toplevel_geometry(geom_name, root.geometry())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
root.attributes("-topmost", False)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
root.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
root.protocol("WM_DELETE_WINDOW", _clear_ref_and_close)
|
||||
_lift_above_main(root, app)
|
||||
|
||||
outer = tk.Frame(root, bg=p["SURFACE"])
|
||||
outer.pack(fill="both", expand=True)
|
||||
_header_bar(outer, p, "Autotext verwalten")
|
||||
|
||||
body = tk.Frame(outer, bg=p["SURFACE"])
|
||||
body.pack(fill="both", expand=True, padx=12, pady=(0, 12))
|
||||
|
||||
btn_bar = tk.Frame(body, bg=p["SURFACE"])
|
||||
btn_bar.pack(side="bottom", fill="x", pady=(10, 0))
|
||||
|
||||
top_part = tk.Frame(body, bg=p["SURFACE"])
|
||||
top_part.pack(fill="both", expand=True)
|
||||
|
||||
tk.Label(
|
||||
top_part,
|
||||
text="Kürzel tippen und mit Leerzeichen, Tab oder Satzzeichen abschließen — "
|
||||
"Ersetzung funktioniert in AzA und (wenn aktiv) global in anderen Programmen.",
|
||||
bg=p["SURFACE"], fg=p["SUBTLE"], font=(FF, 9),
|
||||
wraplength=640, justify="left",
|
||||
).pack(anchor="w", pady=(10, 8))
|
||||
|
||||
data_ref = getattr(app, "_autotext_data", None)
|
||||
if not isinstance(data_ref, dict):
|
||||
messagebox.showerror("Autotext", "Interne Datenbasis fehlt.", parent=root)
|
||||
_clear_ref_and_close()
|
||||
return
|
||||
|
||||
def pull_disk_into_data_ref() -> None:
|
||||
fresh = load_autotext()
|
||||
if not isinstance(fresh, dict):
|
||||
return
|
||||
data_ref["enabled"] = bool(fresh.get("enabled", True))
|
||||
fe = fresh.get("entries") if isinstance(fresh.get("entries"), dict) else {}
|
||||
te = data_ref.setdefault("entries", {})
|
||||
if isinstance(te, dict):
|
||||
te.clear()
|
||||
te.update(fe)
|
||||
else:
|
||||
data_ref["entries"] = dict(fe)
|
||||
fm = fresh.get("entry_meta")
|
||||
if isinstance(fm, dict):
|
||||
tm = data_ref.setdefault("entry_meta", {})
|
||||
if isinstance(tm, dict):
|
||||
tm.clear()
|
||||
tm.update(fm)
|
||||
fts = fresh.get("workspace_backup_ts")
|
||||
if fts is not None:
|
||||
data_ref["workspace_backup_ts"] = fts
|
||||
|
||||
pull_disk_into_data_ref()
|
||||
entries = data_ref.setdefault("entries", {})
|
||||
|
||||
enabled_var = tk.BooleanVar(value=bool(data_ref.get("enabled", True)))
|
||||
|
||||
tk.Checkbutton(
|
||||
top_part, text="Autotext aktiv (In-App und global)",
|
||||
variable=enabled_var,
|
||||
command=lambda: _persist_enabled(),
|
||||
bg=p["SURFACE"], fg=p["TEXT"], font=(FF, 9),
|
||||
activebackground=p["SURFACE"], anchor="w", highlightthickness=0,
|
||||
).pack(fill="x", pady=(0, 10))
|
||||
|
||||
def _persist_enabled() -> None:
|
||||
if not isinstance(data_ref, dict):
|
||||
return
|
||||
data_ref["enabled"] = bool(enabled_var.get())
|
||||
save_autotext(data_ref)
|
||||
schedule_workspace_cloud_push()
|
||||
|
||||
edit_grid = tk.Frame(top_part, bg=p["SURFACE"])
|
||||
edit_grid.pack(fill="x", pady=(0, 6))
|
||||
tk.Label(edit_grid, text="Kürzel", bg=p["SURFACE"], fg=p["TEXT"],
|
||||
font=(FF, 9, "bold")).grid(row=0, column=0, sticky="w")
|
||||
tk.Label(edit_grid, text="Ersetzungstext (mehrzeilig)", bg=p["SURFACE"],
|
||||
fg=p["TEXT"], font=(FF, 9, "bold")).grid(row=0, column=1, sticky="w")
|
||||
abbrev_var = tk.StringVar()
|
||||
ae = tk.Entry(edit_grid, textvariable=abbrev_var, width=18, relief="solid", bd=1,
|
||||
highlightthickness=1, highlightbackground=p["BORDER"],
|
||||
fg=p["TEXT_AREA_FG"], insertbackground=p["TEXT_AREA_FG"],
|
||||
bg=p["TEXT_AREA_BG"], font=(FF, 10))
|
||||
ae.grid(row=1, column=0, sticky="nw", padx=(0, 12), pady=4)
|
||||
repl_box = tk.Text(
|
||||
edit_grid, wrap="word", height=5, width=52, relief="flat", bd=0,
|
||||
highlightthickness=1, highlightbackground=p["BORDER"],
|
||||
bg=p["TEXT_AREA_BG"], fg=p["TEXT_AREA_FG"], font=(FF, 10),
|
||||
padx=6, pady=6,
|
||||
)
|
||||
repl_box.grid(row=1, column=1, sticky="ew", pady=4)
|
||||
edit_grid.columnconfigure(1, weight=1)
|
||||
|
||||
lst_fr = tk.Frame(top_part, bg=p["SURFACE"])
|
||||
lst_fr.pack(fill="both", expand=True, pady=(6, 0))
|
||||
tk.Label(lst_fr, text="Gespeicherte Kürzel", bg=p["SURFACE"],
|
||||
fg=p["TEXT"], font=(FF, 9, "bold")).pack(anchor="w")
|
||||
|
||||
lb_wrap = tk.Frame(lst_fr, bg=p["SURFACE"], highlightthickness=1,
|
||||
highlightbackground=p["BORDER"])
|
||||
lb_wrap.pack(fill="both", expand=False, pady=4)
|
||||
|
||||
scrollbar = tk.Scrollbar(lb_wrap)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
list_height = 5
|
||||
listbox = tk.Listbox(
|
||||
lb_wrap, height=list_height, font=(FF, 10), activestyle="dotbox",
|
||||
yscrollcommand=scrollbar.set,
|
||||
bg=p["TEXT_AREA_BG"], fg=p["TEXT_AREA_FG"],
|
||||
selectbackground=p.get("ACCENT_SOFT", "#E2EEF6"),
|
||||
selectforeground=p.get("TEXT_STRONG", p["TEXT"]),
|
||||
borderwidth=0, highlightthickness=0,
|
||||
)
|
||||
scrollbar.config(command=listbox.yview)
|
||||
listbox.pack(side="left", fill="both", expand=False)
|
||||
|
||||
def refresh_list() -> None:
|
||||
listbox.delete(0, "end")
|
||||
ks = sorted(entries.keys(), key=lambda s: (s.lower(), s))
|
||||
for k in ks:
|
||||
val = entries.get(k, "") or ""
|
||||
short = val.replace("\n", " ")
|
||||
suffix = (" …" + short[-28:]) if len(short) > 48 else ""
|
||||
preview = short[:48] + suffix if len(short) > 48 else short
|
||||
listbox.insert("end", f"«{k}» {preview}")
|
||||
|
||||
def persist() -> None:
|
||||
ent = data_ref.setdefault("entries", {})
|
||||
prune_autotext_meta(data_ref, set(ent.keys()))
|
||||
save_autotext(data_ref)
|
||||
schedule_workspace_cloud_push()
|
||||
refresh_list()
|
||||
|
||||
refresh_list()
|
||||
|
||||
def on_pick(_evt=None) -> None:
|
||||
sel = listbox.curselection()
|
||||
if not sel:
|
||||
return
|
||||
keys_sorted = sorted(entries.keys(), key=lambda s: (s.lower(), s))
|
||||
i = sel[0]
|
||||
if i >= len(keys_sorted):
|
||||
return
|
||||
k = keys_sorted[i]
|
||||
abbrev_var.set(k)
|
||||
repl_box.delete("1.0", "end")
|
||||
repl_box.insert("1.0", entries.get(k, ""))
|
||||
|
||||
listbox.bind("<<ListboxSelect>>", on_pick)
|
||||
|
||||
def reload_ui_full() -> None:
|
||||
pull_disk_into_data_ref()
|
||||
try:
|
||||
enabled_var.set(bool(data_ref.get("enabled", True)))
|
||||
except tk.TclError:
|
||||
pass
|
||||
refresh_list()
|
||||
abbrev_var.set("")
|
||||
try:
|
||||
repl_box.delete("1.0", "end")
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
root._aza_reload_autotext_disk = reload_ui_full
|
||||
|
||||
def do_save_entry() -> None:
|
||||
ab = abbrev_var.get().strip()
|
||||
if not ab:
|
||||
messagebox.showinfo("Autotext", "Bitte ein Kürzel eingeben.", parent=root)
|
||||
return
|
||||
entries[ab] = repl_box.get("1.0", "end").rstrip("\n")
|
||||
touch_autotext_entry_meta(data_ref, ab)
|
||||
persist()
|
||||
getattr(app, "set_status", lambda *_: None)("Autotext gespeichert.")
|
||||
abbrev_var.set("")
|
||||
repl_box.delete("1.0", "end")
|
||||
|
||||
def do_delete_entry() -> None:
|
||||
sel = listbox.curselection()
|
||||
if not sel:
|
||||
messagebox.showinfo("Autotext", "Bitte ein Kürzel in der Liste wählen.",
|
||||
parent=root)
|
||||
return
|
||||
keys_sorted = sorted(entries.keys(), key=lambda s: (s.lower(), s))
|
||||
i = sel[0]
|
||||
if i >= len(keys_sorted):
|
||||
return
|
||||
k_del = keys_sorted[i]
|
||||
if not messagebox.askyesno("Autotext", f"Kürzel «{k_del}» wirklich löschen?",
|
||||
parent=root):
|
||||
return
|
||||
entries.pop(k_del, None)
|
||||
em = data_ref.get("entry_meta")
|
||||
if isinstance(em, dict):
|
||||
em.pop(k_del, None)
|
||||
persist()
|
||||
abbrev_var.set("")
|
||||
repl_box.delete("1.0", "end")
|
||||
|
||||
def _diktat_toggle() -> None:
|
||||
if not dik_state["active"]:
|
||||
if not callable(getattr(app, "ensure_ready", None)):
|
||||
return
|
||||
if not app.ensure_ready():
|
||||
messagebox.showinfo(
|
||||
"Diktat",
|
||||
"KI-Verbindung noch nicht bereit.",
|
||||
parent=root,
|
||||
)
|
||||
return
|
||||
if callable(getattr(app, "_check_ai_consent", None)):
|
||||
try:
|
||||
if not app._check_ai_consent():
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
mic_fn = getattr(app, "_ensure_microphone_ready", None)
|
||||
recorder = getattr(app, "recorder", None)
|
||||
transcribe_fn = getattr(app, "transcribe_wav", None)
|
||||
if recorder is None or transcribe_fn is None or not callable(mic_fn):
|
||||
messagebox.showinfo("Mikrofon", "Recorder nicht verfügbar.", parent=root)
|
||||
return
|
||||
if not mic_fn():
|
||||
return
|
||||
try:
|
||||
recorder.start()
|
||||
except Exception as exc:
|
||||
messagebox.showwarning("Aufnahme", str(exc), parent=root)
|
||||
return
|
||||
dik_state["active"] = True
|
||||
try:
|
||||
btn_diktat.configure(text="Stoppen")
|
||||
except tk.TclError:
|
||||
pass
|
||||
return
|
||||
|
||||
dik_state["active"] = False
|
||||
try:
|
||||
btn_diktat.configure(text="Diktat")
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
wav_path = None
|
||||
try:
|
||||
rec = getattr(app, "recorder", None)
|
||||
if rec is None:
|
||||
return
|
||||
wav_path = rec.stop_and_save_wav()
|
||||
except Exception:
|
||||
return
|
||||
|
||||
def worker():
|
||||
try:
|
||||
from aza_audio import persist_audio_safe
|
||||
|
||||
transcribe_fn = getattr(app, "transcribe_wav", None)
|
||||
if wav_path is None or transcribe_fn is None:
|
||||
return
|
||||
safe_path = None
|
||||
try:
|
||||
safe_path = persist_audio_safe(wav_path)
|
||||
except Exception:
|
||||
safe_path = wav_path
|
||||
text_raw = transcribe_fn(safe_path)
|
||||
raw = text_raw if isinstance(text_raw, str) else str(text_raw or "")
|
||||
txt = raw.strip()
|
||||
|
||||
dik_apply = getattr(app, "_diktat_apply_punctuation", None)
|
||||
if callable(dik_apply):
|
||||
try:
|
||||
txt = dik_apply(txt)
|
||||
except Exception:
|
||||
pass
|
||||
if safe_path and safe_path != wav_path:
|
||||
try:
|
||||
import os
|
||||
os.remove(wav_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def ins() -> None:
|
||||
try:
|
||||
if root.winfo_exists():
|
||||
chunk = (txt or "").strip()
|
||||
if chunk:
|
||||
cur = repl_box.get("1.0", "end").rstrip()
|
||||
if not cur:
|
||||
repl_box.insert("1.0", chunk)
|
||||
else:
|
||||
if not cur.endswith("\n"):
|
||||
repl_box.insert(tk.END, "\n")
|
||||
repl_box.insert(tk.END, chunk)
|
||||
repl_box.see(tk.INSERT)
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
app.after(0, ins)
|
||||
except Exception as exc_inner:
|
||||
|
||||
def err() -> None:
|
||||
messagebox.showerror(
|
||||
"Fehler bei Transkription", str(exc_inner), parent=root,
|
||||
)
|
||||
|
||||
app.after(0, err)
|
||||
|
||||
threading.Thread(target=worker, daemon=True).start()
|
||||
|
||||
tk.Button(btn_bar, text="Speichern / Aktualisieren", command=do_save_entry,
|
||||
bg=p["ACCENT"], fg="white", font=(FF, 9), relief="flat", padx=12, pady=6,
|
||||
cursor="hand2", activebackground="#4A7A9E",
|
||||
).pack(side="left", padx=(0, 6))
|
||||
|
||||
btn_diktat = tk.Button(
|
||||
btn_bar, text="Diktat", command=_diktat_toggle,
|
||||
bg=p["SURFACE"], fg=p["TEXT"], font=(FF, 9), relief="solid", bd=1,
|
||||
padx=10, pady=5, cursor="hand2",
|
||||
)
|
||||
btn_diktat.pack(side="left", padx=(0, 6))
|
||||
|
||||
tk.Button(btn_bar, text="Löschen", command=do_delete_entry,
|
||||
bg=p["SURFACE"], fg=p["TEXT"], font=(FF, 9),
|
||||
relief="solid", bd=1, padx=10, pady=5, cursor="hand2",
|
||||
).pack(side="left", padx=(0, 6))
|
||||
tk.Button(btn_bar, text="Schließen", command=_clear_ref_and_close,
|
||||
bg=p["SURFACE"], fg=p["SUBTLE"], font=(FF, 9),
|
||||
relief="solid", bd=1, padx=10, pady=5, cursor="hand2",
|
||||
).pack(side="right")
|
||||
|
||||
|
||||
def _tb_windows_map(app):
|
||||
d = getattr(app, _TB_WIN_DICT_ATTR, None)
|
||||
if not isinstance(d, dict):
|
||||
d = {}
|
||||
setattr(app, _TB_WIN_DICT_ATTR, d)
|
||||
return d
|
||||
|
||||
|
||||
def open_workspace_textblock_editor(app, slot_key: str) -> None:
|
||||
from aza_ui_helpers import (
|
||||
center_window,
|
||||
load_toplevel_geometry,
|
||||
save_toplevel_geometry,
|
||||
)
|
||||
from aza_persistence import load_textbloecke, save_textbloecke
|
||||
from aza_workspace_sync import schedule_workspace_cloud_push, utc_now_iso
|
||||
|
||||
p = _palette_for_app(app)
|
||||
key = str(slot_key).strip()
|
||||
if not key.isdigit():
|
||||
return
|
||||
|
||||
wins = _tb_windows_map(app)
|
||||
prev = wins.get(key)
|
||||
if prev is not None:
|
||||
try:
|
||||
if prev.winfo_exists():
|
||||
prev.deiconify()
|
||||
_lift_above_main(prev, app)
|
||||
return
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
data = load_textbloecke()
|
||||
slot = dict(data.get(key) or {})
|
||||
|
||||
root = tk.Toplevel(app)
|
||||
wins[key] = root
|
||||
|
||||
root.title(slot.get("name") or f"Textblock {key}")
|
||||
root.configure(bg=p["SURFACE"])
|
||||
root.minsize(500, 360)
|
||||
root.transient(app)
|
||||
_register_if_any(app, root)
|
||||
|
||||
geom_name = f"workspace_tb_{key}"
|
||||
saved = load_toplevel_geometry(geom_name)
|
||||
if saved:
|
||||
try:
|
||||
root.geometry(saved)
|
||||
except tk.TclError:
|
||||
root.geometry("660x520")
|
||||
center_window(root, 660, 520)
|
||||
else:
|
||||
root.geometry("660x520")
|
||||
center_window(root, 660, 520)
|
||||
|
||||
def _close_tb() -> None:
|
||||
wm = _tb_windows_map(app)
|
||||
if wm.get(key) is root:
|
||||
wm.pop(key, None)
|
||||
try:
|
||||
save_toplevel_geometry(geom_name, root.geometry())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
root.attributes("-topmost", False)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
root.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
root.protocol("WM_DELETE_WINDOW", _close_tb)
|
||||
_lift_above_main(root, app)
|
||||
|
||||
outer = tk.Frame(root, bg=p["SURFACE"])
|
||||
outer.pack(fill="both", expand=True)
|
||||
|
||||
ttl = slot.get("name") or f"Textblock {key}"
|
||||
_header_bar(outer, p, ttl)
|
||||
|
||||
body = tk.Frame(outer, bg=p["SURFACE"])
|
||||
body.pack(fill="both", expand=True, padx=14, pady=12)
|
||||
|
||||
tk.Label(body, text="Anzeigename", bg=p["SURFACE"], fg=p["TEXT"],
|
||||
font=(FF, 9, "bold")).pack(anchor="w")
|
||||
name_var = tk.StringVar(value=str(slot.get("name") or ttl))
|
||||
tk.Entry(body, textvariable=name_var, width=40,
|
||||
relief="solid", bd=1, bg=p["TEXT_AREA_BG"], fg=p["TEXT_AREA_FG"],
|
||||
insertbackground=p["TEXT_AREA_FG"],
|
||||
highlightthickness=1, highlightbackground=p["BORDER"],
|
||||
font=(FF, 10)).pack(fill="x", pady=(2, 10))
|
||||
|
||||
tk.Label(body, text="Inhalt", bg=p["SURFACE"], fg=p["TEXT"],
|
||||
font=(FF, 9, "bold")).pack(anchor="w")
|
||||
txt = ScrolledText(
|
||||
body, wrap="word", height=16, font=(FF, 10),
|
||||
bg=p["TEXT_AREA_BG"], fg=p["TEXT_AREA_FG"],
|
||||
insertbackground=p["TEXT_AREA_FG"],
|
||||
relief="flat", bd=0, highlightthickness=1,
|
||||
highlightbackground=p["BORDER"], padx=8, pady=8,
|
||||
)
|
||||
txt.pack(fill="both", expand=True, pady=(4, 10))
|
||||
txt.insert("1.0", slot.get("content") or "")
|
||||
|
||||
def do_save() -> None:
|
||||
fresh = load_textbloecke()
|
||||
nm = name_var.get().strip() or f"Textblock {key}"
|
||||
fresh[key] = {
|
||||
"name": nm,
|
||||
"content": txt.get("1.0", "end").rstrip("\n"),
|
||||
"updated_at": utc_now_iso(),
|
||||
}
|
||||
save_textbloecke(fresh)
|
||||
schedule_workspace_cloud_push()
|
||||
shell = getattr(app, "_aza_office_v1", None)
|
||||
if shell is not None and hasattr(shell, "refresh_sidebar_textbloecke_section"):
|
||||
try:
|
||||
shell.refresh_sidebar_textbloecke_section()
|
||||
except Exception:
|
||||
pass
|
||||
getattr(app, "set_status", lambda *_: None)(f"Textblock {key} gespeichert.")
|
||||
try:
|
||||
root.title(nm)
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
bar_fr = tk.Frame(body, bg=p["SURFACE"])
|
||||
bar_fr.pack(fill="x")
|
||||
tk.Button(bar_fr, text="Speichern", command=do_save,
|
||||
bg=p["ACCENT"], fg="white", font=(FF, 9), relief="flat",
|
||||
padx=14, pady=6, cursor="hand2",
|
||||
).pack(side="left", padx=(0, 8))
|
||||
tk.Button(bar_fr, text="Schließen", command=_close_tb,
|
||||
bg=p["SURFACE"], fg=p["SUBTLE"], font=(FF, 9), relief="solid",
|
||||
bd=1, padx=12, pady=5, cursor="hand2",
|
||||
).pack(side="left")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"open_workspace_autotext_manager",
|
||||
"open_workspace_textblock_editor",
|
||||
]
|
||||
Reference in New Issue
Block a user