Stock-Tool/activation_dialog.py

200 lines
7.0 KiB
Python

"""
Activation Dialog
=================
Shown on first launch when no license key is stored.
Collects the license key, verifies it against Supabase, and on success
saves it to disk so it won't be asked again.
"""
import tkinter as tk
import license_check
# ---- Colors — exact match to the main app theme ----
_BG = "#000000"
_ACCENT = "#00e676"
_TEXT = "#ffffff"
_SUBTEXT = "#555555"
_ERROR = "#ff4545"
_SUCCESS = "#00e676"
_BORDER = "#0d0d0d"
# ---- Shared pill-shape helper ------------------------------------------------
def _pill_pts(w, h, r=10):
return [r,0, w-r,0, w,0, w,r, w,h-r, w,h, w-r,h, r,h, 0,h, 0,h-r, 0,r, 0,0]
# ---- Pill entry (borderless green canvas entry) ------------------------------
def _make_pill_entry(parent, textvariable, width_px=380, height_px=40,
font=("Consolas", 11)):
"""Return a (frame, entry) tuple — a pill-shaped green entry."""
r = height_px // 2
frame = tk.Frame(parent, bg=_BG, bd=0, highlightthickness=0)
cv = tk.Canvas(frame, width=width_px, height=height_px,
highlightthickness=0, bd=0, bg=_BG)
cv.pack()
pts = _pill_pts(width_px, height_px, r)
cv.create_polygon(pts, smooth=True, fill=_ACCENT, outline=_ACCENT, width=0)
inner = tk.Frame(cv, bg=_ACCENT, bd=0, highlightthickness=0)
cv.create_window(width_px // 2, height_px // 2, window=inner,
width=width_px - 10, height=height_px - 8)
entry = tk.Entry(inner, textvariable=textvariable,
font=font, bg=_ACCENT, fg="#000000",
insertbackground="#000000", relief="flat",
bd=0, highlightthickness=0)
entry.pack(fill="x", expand=True, padx=8, ipady=2)
return frame, entry
# ---- Pill button (borderless canvas button) ----------------------------------
class _PillBtn(tk.Canvas):
def __init__(self, parent, text, command,
width=120, height=36, radius=10,
bg=_ACCENT, fg="#000000"):
super().__init__(parent, width=width, height=height,
highlightthickness=0, bd=0, bg=parent.cget("bg"))
self._bg = bg
self._fg = fg
self._command = command
self._enabled = True
pts = _pill_pts(width, height, radius)
self._rect = self.create_polygon(pts, smooth=True,
fill=bg, outline=bg, width=0)
self._lbl = self.create_text(width // 2, height // 2,
text=text, fill=fg,
font=("Segoe UI", 10, "bold"))
self.config(cursor="hand2")
self.bind("<Button-1>", lambda _e: self._command() if self._enabled else None)
self.bind("<Enter>", lambda _e: self.itemconfig(self._rect, fill="#00c060") if self._enabled else None)
self.bind("<Leave>", lambda _e: self.itemconfig(self._rect, fill=self._bg))
def set_enabled(self, enabled: bool):
self._enabled = enabled
self.itemconfig(self._rect, fill=self._bg if enabled else _BORDER)
self.itemconfig(self._lbl, fill=self._fg if enabled else _SUBTEXT)
self.config(cursor="hand2" if enabled else "")
def set_text(self, text: str):
self.itemconfig(self._lbl, text=text)
# ---- Main dialog ------------------------------------------------------------
def run_activation(hwid: str) -> bool:
"""
Open the activation dialog and block until the user either activates
successfully or closes the window.
Returns True if activated, False if the user cancelled / closed.
"""
activated = [False]
root = tk.Tk()
root.title("Ultimate Investment Tool — License Activation")
root.configure(bg=_BG)
root.resizable(False, False)
# Center the window
root.update_idletasks()
w, h = 480, 340
x = (root.winfo_screenwidth() - w) // 2
y = (root.winfo_screenheight() - h) // 2
root.geometry(f"{w}x{h}+{x}+{y}")
root.protocol("WM_DELETE_WINDOW", lambda: _on_exit(root, activated))
# ---- Header accent bar ----
tk.Frame(root, bg=_ACCENT, height=4).pack(fill="x")
tk.Label(root, text="Ultimate Investment Tool",
font=("Segoe UI", 16, "bold"),
bg=_BG, fg=_TEXT).pack(pady=(28, 4))
tk.Label(root, text="Enter your license key to activate this software.",
font=("Segoe UI", 9),
bg=_BG, fg=_SUBTEXT).pack()
# ---- Pill key entry ----
key_var = tk.StringVar()
ef, key_entry = _make_pill_entry(root, key_var, width_px=400, height_px=42)
ef.pack(pady=22)
key_entry.focus()
# ---- Status label ----
status_var = tk.StringVar(value="")
status_label = tk.Label(root, textvariable=status_var,
font=("Segoe UI", 9),
bg=_BG, fg=_ERROR, wraplength=420)
status_label.pack(pady=(0, 8))
# ---- Pill buttons ----
btn_frame = tk.Frame(root, bg=_BG)
btn_frame.pack(pady=4)
activate_btn = _PillBtn(btn_frame, "Activate",
command=lambda: _on_activate(
root, key_var, status_var, status_label,
activate_btn, activated, hwid),
width=130, height=38, bg=_ACCENT, fg="#000000")
activate_btn.grid(row=0, column=0, padx=10)
exit_btn = _PillBtn(btn_frame, "Exit",
command=lambda: _on_exit(root, activated),
width=110, height=38, bg=_BORDER, fg=_SUBTEXT)
exit_btn.grid(row=0, column=1, padx=10)
root.bind("<Return>", lambda e: _on_activate(
root, key_var, status_var, status_label, activate_btn, activated, hwid))
# ---- Footer ----
tk.Label(root, text="Need a license? Contact us to subscribe.",
font=("Segoe UI", 8),
bg=_BG, fg=_SUBTEXT).pack(side="bottom", pady=14)
root.mainloop()
return activated[0]
def _on_activate(root, key_var, status_var, status_label,
activate_btn, activated, hwid):
key = key_var.get().strip()
if not key:
status_var.set("Please enter a license key.")
return
activate_btn.set_enabled(False)
activate_btn.set_text("Checking…")
status_var.set("Contacting license server…")
status_label.config(fg=_SUBTEXT)
root.update()
try:
valid, reason = license_check.verify_license(key, hwid)
except Exception as exc:
activate_btn.set_enabled(True)
activate_btn.set_text("Activate")
status_var.set(f"Error: {exc}")
status_label.config(fg=_ERROR)
return
if valid:
license_check.save_license_key(key, hwid)
status_var.set("Activated successfully! Launching…")
status_label.config(fg=_SUCCESS)
activated[0] = True
root.after(900, root.destroy)
else:
activate_btn.set_enabled(True)
activate_btn.set_text("Activate")
status_var.set(reason)
status_label.config(fg=_ERROR)
def _on_exit(root, activated):
activated[0] = False
root.destroy()