From a44d2afca5923715f41f347073fb0d84260bc2c2 Mon Sep 17 00:00:00 2001 From: MCLauncher Date: Thu, 6 Aug 2026 22:04:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20v1.3.0-Beta=20PCL=E9=A3=8E=E6=A0=BCUI?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 固定窗口尺寸 980x620, 不可缩放, 启动按钮不再被截出屏幕 - 横向布局: 左侧深蓝导航栏 + 右侧内容区 - 启动页改为左右两列: 设置/版本 + 日志/启动按钮 - 导航按钮高亮切换, 模拟 PCL 风格 --- launcher.py | 149 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 99 insertions(+), 50 deletions(-) diff --git a/launcher.py b/launcher.py index 05850a3..2b2c525 100644 --- a/launcher.py +++ b/launcher.py @@ -564,8 +564,8 @@ class MeowLauncherApp: def __init__(self): self.root = tk.Tk() self.root.title("MeowLauncher %s - Minecraft 启动器" % VERSION) - self.root.geometry("600x780") - self.root.minsize(540, 680) + self.root.geometry("980x620") + self.root.resizable(False, False) self.root.configure(bg=self.BG) self.msg_queue = queue.Queue() @@ -615,6 +615,20 @@ class MeowLauncherApp: style.map("TNotebook.Tab", background=[("selected", self.ACCENT)], foreground=[("selected", "#ffffff")]) + style.configure("Content.TFrame", background=self.BG) + style.configure("Nav.TFrame", background="#1e3a8a") + style.configure("NavLogo.TLabel", background="#1e3a8a", foreground="#ffffff", + font=("Microsoft YaHei UI", 30)) + style.configure("NavTitle.TLabel", background="#1e3a8a", foreground="#ffffff", + font=("Microsoft YaHei UI", 15, "bold")) + style.configure("NavSub.TLabel", background="#1e3a8a", foreground="#bfdbfe", + font=("Microsoft YaHei UI", 9)) + style.configure("NavButton.TButton", background="#1e3a8a", foreground="#e0e7ff", + font=("Microsoft YaHei UI", 11), borderwidth=0, padding=(14, 10)) + style.map("NavButton.TButton", + background=[("active", "#2563eb"), ("pressed", "#1d4ed8")]) + style.configure("NavButtonActive.TButton", background="#2563eb", foreground="#ffffff", + font=("Microsoft YaHei UI", 11, "bold"), borderwidth=0, padding=(14, 10)) style.configure("Treeview", background=self.CARD, fieldbackground=self.CARD, foreground=self.TEXT) style.configure("Treeview.Heading", background="#eef2f9", foreground=self.TEXT) @@ -632,106 +646,141 @@ class MeowLauncherApp: return frame def _build_ui(self): - """构建主窗口: 标题 + 标签页(启动器/资源中心)。""" - ttk.Label(self.root, text="MeowLauncher", style="Title.TLabel").pack(pady=(14, 0)) - ttk.Label(self.root, text="跨平台 Minecraft 启动器 v%s" % VERSION, style="Dim.TLabel").pack() + """构建主窗口: 左侧导航 + 右侧内容页。""" + self.nav = ttk.Frame(self.root, style="Nav.TFrame", width=200) + self.nav.pack(side="left", fill="y") + self.nav.pack_propagate(False) - self.notebook = ttk.Notebook(self.root) - self.notebook.pack(fill="both", expand=True, padx=10, pady=8) + ttk.Label(self.nav, text="🐱", style="NavLogo.TLabel").pack(pady=(26, 0)) + ttk.Label(self.nav, text="MeowLauncher", style="NavTitle.TLabel").pack(pady=(4, 2)) + ttk.Label(self.nav, text="跨平台 MC 启动器", style="NavSub.TLabel").pack(pady=(0, 26)) - self.tab_launch = ttk.Frame(self.notebook) - self.tab_res = ttk.Frame(self.notebook) - self.notebook.add(self.tab_launch, text=" 启动器 ") - self.notebook.add(self.tab_res, text=" 资源中心 ") + self.nav_launch_btn = ttk.Button(self.nav, text=" 🚀 启动游戏", + style="NavButton.TButton", + command=lambda: self._switch_page("launch")) + self.nav_launch_btn.pack(fill="x", padx=16, pady=3) + self.nav_res_btn = ttk.Button(self.nav, text=" 📦 资源中心", + style="NavButton.TButton", + command=lambda: self._switch_page("res")) + self.nav_res_btn.pack(fill="x", padx=16, pady=3) + + ttk.Label(self.nav, text="v%s" % VERSION, style="NavSub.TLabel").pack(side="bottom", pady=16) + + self.content = ttk.Frame(self.root, style="Content.TFrame") + self.content.pack(side="left", fill="both", expand=True) + self.page_launch = ttk.Frame(self.content, style="Content.TFrame") + self.page_res = ttk.Frame(self.content, style="Content.TFrame") self._build_launch_tab() self._build_res_tab() + self.page_launch.pack(fill="both", expand=True) + self._set_nav_active("launch") + + def _set_nav_active(self, page): + """导航按钮高亮切换。""" + if page == "launch": + self.nav_launch_btn.configure(style="NavButtonActive.TButton") + self.nav_res_btn.configure(style="NavButton.TButton") + else: + self.nav_launch_btn.configure(style="NavButton.TButton") + self.nav_res_btn.configure(style="NavButtonActive.TButton") + + def _switch_page(self, page): + """切换页面 (左侧导航)。""" + if page == "launch": + self.page_res.pack_forget() + self.page_launch.pack(fill="both", expand=True) + else: + self.page_launch.pack_forget() + self.page_res.pack(fill="both", expand=True) + self._set_nav_active(page) def _build_launch_tab(self): - """构建[启动器]页: 登录、版本、设置、日志、启动按钮。""" - login = self._card(self.tab_launch, "账号登录") + """构建[启动游戏]页: 左列设置 + 右列日志与启动按钮 (PCL 风格)。""" + body = ttk.Frame(self.page_launch, style="Content.TFrame") + body.pack(fill="both", expand=True, padx=16, pady=14) + left = ttk.Frame(body, style="Content.TFrame") + left.pack(side="left", fill="both", expand=True) + right = ttk.Frame(body, style="Content.TFrame") + right.pack(side="left", fill="both", expand=True, padx=(14, 0)) + + login = self._card(left, "账号登录") self.login_mode = tk.StringVar(value="offline") row = ttk.Frame(login, style="Card.TFrame") row.pack(fill="x", pady=4) ttk.Radiobutton(row, text="离线模式", variable=self.login_mode, value="offline", command=self._toggle_login).pack(side="left", padx=(0, 16)) - ttk.Radiobutton(row, text="第三方登录 (Yggdrasil)", - variable=self.login_mode, value="yggdrasil", + ttk.Radiobutton(row, text="第三方登录", variable=self.login_mode, value="yggdrasil", command=self._toggle_login).pack(side="left") self.offline_frame = ttk.Frame(login, style="Card.TFrame") self.offline_frame.pack(fill="x") ttk.Label(self.offline_frame, text="玩家名:", style="Card.TLabel").pack(side="left") - self.offline_name = ttk.Entry(self.offline_frame, width=24) + self.offline_name = ttk.Entry(self.offline_frame, width=22) self.offline_name.pack(side="left", padx=8) self.ygg_frame = ttk.Frame(login, style="Card.TFrame") self.ygg_frame.pack(fill="x") self.ygg_frame.pack_forget() - ttk.Label(self.ygg_frame, text="皮肤站:", style="Card.TLabel").grid(row=0, column=0, sticky="w", pady=2) self.server_var = tk.StringVar(value="LittleSkin") self.server_box = ttk.Combobox(self.ygg_frame, textvariable=self.server_var, - values=list(YggdrasilAuth.PRESETS.keys()), state="readonly", width=22) + values=list(YggdrasilAuth.PRESETS.keys()), state="readonly", width=18) self.server_box.grid(row=0, column=1, sticky="w", pady=2) - - ttk.Label(self.ygg_frame, text="邮箱/用户名:", style="Card.TLabel").grid(row=1, column=0, sticky="w", pady=2) - self.ygg_user = ttk.Entry(self.ygg_frame, width=24) + ttk.Label(self.ygg_frame, text="账号:", style="Card.TLabel").grid(row=1, column=0, sticky="w", pady=2) + self.ygg_user = ttk.Entry(self.ygg_frame, width=18) self.ygg_user.grid(row=1, column=1, sticky="w", pady=2) - ttk.Label(self.ygg_frame, text="密码:", style="Card.TLabel").grid(row=2, column=0, sticky="w", pady=2) - self.ygg_pass = ttk.Entry(self.ygg_frame, width=24, show="*") + self.ygg_pass = ttk.Entry(self.ygg_frame, width=18, show="*") self.ygg_pass.grid(row=2, column=1, sticky="w", pady=2) - ttk.Label(self.ygg_frame, text="支持 LittleSkin 等 authlib-injector 皮肤站", - style="Card.TLabel").grid(row=3, column=0, columnspan=2, sticky="w", pady=2) - - ver = self._card(self.tab_launch, "游戏版本") + ver = self._card(left, "游戏版本") row = ttk.Frame(ver, style="Card.TFrame") row.pack(fill="x") self.version_var = tk.StringVar() - self.version_box = ttk.Combobox(row, textvariable=self.version_var, state="readonly", width=28) + self.version_box = ttk.Combobox(row, textvariable=self.version_var, state="readonly", width=22) self.version_box.pack(side="left") ttk.Button(row, text="刷新", command=self.refresh_versions).pack(side="left", padx=8) self.show_snapshot = tk.BooleanVar(value=False) ttk.Checkbutton(ver, text="显示快照版", variable=self.show_snapshot, command=self.refresh_versions).pack(anchor="w", pady=(6, 0)) - cfg = self._card(self.tab_launch, "启动设置") + cfg = self._card(left, "启动设置") row = ttk.Frame(cfg, style="Card.TFrame") row.pack(fill="x", pady=3) - ttk.Label(row, text="最大内存 (MB):", style="Card.TLabel").pack(side="left") + ttk.Label(row, text="内存 (MB):", style="Card.TLabel").pack(side="left") self.mem_var = tk.StringVar(value="2048") - ttk.Entry(row, textvariable=self.mem_var, width=10).pack(side="left", padx=8) - + ttk.Entry(row, textvariable=self.mem_var, width=8).pack(side="left", padx=8) row = ttk.Frame(cfg, style="Card.TFrame") row.pack(fill="x", pady=3) - ttk.Label(row, text="Java 路径 (留空自动检测):", style="Card.TLabel").pack(side="left") + ttk.Label(row, text="Java 路径:", style="Card.TLabel").pack(side="left") self.java_var = tk.StringVar() - ttk.Entry(row, textvariable=self.java_var, width=16).pack(side="left", padx=8) + ttk.Entry(row, textvariable=self.java_var, width=14).pack(side="left", padx=8) ttk.Button(row, text="浏览", command=self._pick_java).pack(side="left", padx=4) - row = ttk.Frame(cfg, style="Card.TFrame") row.pack(fill="x", pady=3) ttk.Label(row, text="游戏目录:", style="Card.TLabel").pack(side="left") self.dir_var = tk.StringVar(value=str(Path.home() / ".minecraft")) - ttk.Entry(row, textvariable=self.dir_var, width=16).pack(side="left", padx=8) + ttk.Entry(row, textvariable=self.dir_var, width=14).pack(side="left", padx=8) ttk.Button(row, text="浏览", command=self._pick_dir).pack(side="left", padx=4) - log_card = self._card(self.tab_launch, "日志") - self.log_box = scrolledtext.ScrolledText(log_card, height=8, bg="#f8fafc", + log_card = self._card(right, "日志") + self.log_box = scrolledtext.ScrolledText(log_card, height=10, bg="#f8fafc", fg=self.TEXT, font=("Consolas", 9), relief="flat", state="disabled") - self.log_box.pack(fill="x") + self.log_box.pack(fill="both", expand=True) self.progress = ttk.Progressbar(log_card, mode="determinate", maximum=100) self.progress.pack(fill="x", pady=(8, 0)) - self.launch_btn = ttk.Button(self.tab_launch, text="启动游戏", style="Accent.TButton", + self.launch_btn = ttk.Button(right, text="启动游戏", style="Accent.TButton", command=self._on_launch) - self.launch_btn.pack(fill="x", padx=14, pady=14) + self.launch_btn.pack(fill="x", pady=(12, 0)) def _build_res_tab(self): """构建[资源中心]页: 版本一键下载 + Mod/插件搜索下载。""" - dver = self._card(self.tab_res, "游戏版本下载") + body = ttk.Frame(self.page_res, style="Content.TFrame") + body.pack(fill="both", expand=True, padx=16, pady=14) + + dver = self._card(body, "游戏版本下载") row = ttk.Frame(dver, style="Card.TFrame") row.pack(fill="x") self.res_version_var = tk.StringVar() @@ -740,7 +789,7 @@ class MeowLauncherApp: ttk.Button(row, text="刷新列表", command=self.refresh_versions).pack(side="left", padx=6) ttk.Button(row, text="一键下载", command=self._download_version_only).pack(side="left", padx=6) - dmod = self._card(self.tab_res, "Mod / 插件下载 (Modrinth)") + dmod = self._card(body, "Mod / 插件下载 (Modrinth)") row = ttk.Frame(dmod, style="Card.TFrame") row.pack(fill="x") self.res_query = ttk.Entry(row, width=14) @@ -763,18 +812,18 @@ class MeowLauncherApp: self.res_tree.heading("title", text="名称") self.res_tree.heading("author", text="作者") self.res_tree.heading("downloads", text="下载量") - self.res_tree.column("title", width=190) - self.res_tree.column("author", width=90) - self.res_tree.column("downloads", width=80) + self.res_tree.column("title", width=280) + self.res_tree.column("author", width=130) + self.res_tree.column("downloads", width=100) self.res_tree.pack(fill="x", pady=6) ttk.Button(dmod, text="下载选中项", command=self._download_selected).pack(anchor="w") - self.res_log = scrolledtext.ScrolledText(self.tab_res, height=5, bg="#f8fafc", + self.res_log = scrolledtext.ScrolledText(body, height=5, bg="#f8fafc", fg=self.TEXT, font=("Consolas", 9), relief="flat", state="disabled") - self.res_log.pack(fill="x", padx=14, pady=(4, 6)) - self.res_progress = ttk.Progressbar(self.tab_res, mode="determinate", maximum=100) - self.res_progress.pack(fill="x", padx=14, pady=(0, 10)) + self.res_log.pack(fill="x", pady=(6, 0)) + self.res_progress = ttk.Progressbar(body, mode="determinate", maximum=100) + self.res_progress.pack(fill="x", pady=(8, 0)) def _res_append(self, msg): """向资源中心日志框追加一行。"""