200 lines
7.0 KiB
Python
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()
|