Skip to content

Commit

Permalink
Merge pull request #13 from cristian64/revised_menu_bar
Browse files Browse the repository at this point in the history
Revised menu bar.
  • Loading branch information
RenolY2 authored Dec 14, 2023
2 parents 470642f + 567f311 commit 5501d8b
Show file tree
Hide file tree
Showing 3 changed files with 361 additions and 10 deletions.
50 changes: 40 additions & 10 deletions mkdd_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
import os
import platform
import signal
import subprocess
import sys
import textwrap
import tkinter
import webbrowser

import customtkinter

from PIL import Image, ImageTk

from src import (
CTkDropdownMenu,
CTkMenuBar,
CTkToolTip,
patcher,
)
Expand Down Expand Up @@ -49,10 +51,6 @@ def __init__(self):
logo = ImageTk.PhotoImage(file=get_icon_path('logo', ICON_RESOLUTIONS[-1]))
self.iconphoto(False, logo)

menu = tkinter.Menu(self)
menu.add_command(label='About', command=self._show_about_dialog)
self.config(menu=menu)

font_width, font_height = get_font_metrics()
padding = int(font_width * 1.75)
spacing = int(font_width * 0.75)
Expand All @@ -61,15 +59,39 @@ def __init__(self):
self.geometry(config['geometry']['window'])
else:
self.geometry(f'{font_width * 100}x{font_height * 20}')
self.minsize(font_width * 50, font_height * 12)
self.minsize(font_width * 50, font_height * 15)

self.grid_rowconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure((0, 1), weight=1)

menu_bar = CTkMenuBar.CTkMenuBar(
master=self,
bg_color='#070707',
height=int(font_height * 1.5),
padx=font_height // 2,
pady=font_height // 3,
)
menu_bar.grid(row=0, column=0, columnspan=2, sticky='nsew')
file_button = menu_bar.add_cascade('File')
file_menu = CTkDropdownMenu.CustomDropdownMenu(widget=file_button,
height=int(font_height * 1.5),
corner_radius=0,
border_color='#111111',
separator_color='#1C1C1C',
hover_color='#323232',
font=None,
padx=0,
pady=0)
file_menu.add_option('Open Configuration Directory...', command=self._open_config_directory)
file_menu.add_separator()
file_menu.add_option('Quit', command=self.close)
about_button = menu_bar.add_cascade('About')
about_button.configure(command=self._show_about_dialog)

main_frame = customtkinter.CTkFrame(master=self, fg_color='transparent')
main_frame.grid_rowconfigure(2, weight=1)
main_frame.grid_columnconfigure(1, weight=1)
main_frame.grid(row=0,
main_frame.grid(row=1,
column=0,
columnspan=2,
padx=padding,
Expand Down Expand Up @@ -107,7 +129,7 @@ def __init__(self):
self.folder_mode_checkbox = customtkinter.CTkCheckBox(master=self,
text='Folder Mode',
command=self._sync_form)
self.folder_mode_checkbox.grid(row=1,
self.folder_mode_checkbox.grid(row=2,
column=0,
padx=padding,
pady=(padding, padding),
Expand All @@ -127,7 +149,7 @@ def __init__(self):
justify='left')

self.patch_button = customtkinter.CTkButton(master=self, text='Patch', command=self.patch)
self.patch_button.grid(row=1, column=1, padx=padding, pady=(padding, padding), sticky='ns')
self.patch_button.grid(row=2, column=1, padx=padding, pady=(padding, padding), sticky='ns')

self.input_iso_entry.insert(0, self._last_input_iso)
self.output_iso_entry.insert(0, self._last_output_iso)
Expand Down Expand Up @@ -319,6 +341,14 @@ def _save_config(self):
with open(get_config_path(), 'w', encoding='utf-8') as f:
config.write(f)

def _open_config_directory(self):
config_dir = os.path.dirname(get_config_path())
if platform.system() == 'Windows':
os.startfile(config_dir) # pylint: disable=no-member
else:
subprocess.check_call(
('open' if platform.system() == 'Darwin' else 'xdg-open', config_dir))

def _show_about_dialog(self):
text = textwrap.dedent(f'MKDD Patcher {patcher.__version__} by Yoshi2')
URL = 'https://github.com/RenolY2/mkdd-track-patcher'
Expand Down
253 changes: 253 additions & 0 deletions src/CTkDropdownMenu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
# https://github.com/Akascape/CTkMenuBar
"""
Custom Dropdown Menu for CTkMenuBar
Original Author: LucianoSaldivia | https://github.com/LucianoSaldivia
Modified by: Akash Bora (Akascape)
"""

from __future__ import annotations
import customtkinter
from functools import partial
import tkinter as tk
from typing import Callable

class _CDMOptionButton(customtkinter.CTkButton):
def setParentMenu(self, menu: "CustomDropdownMenu"):
self.parent_menu = menu

class _CDMSubmenuButton(_CDMOptionButton):
def setSubmenu(self, submenu: "CustomDropdownMenu"):
self.submenu = submenu

class CustomDropdownMenu(customtkinter.CTkFrame):

def __init__(self,
widget: customtkinter.CTkBaseClass | _CDMSubmenuButton,
master: any = None,
border_width: int = 1,
width: int = 150,
height: int = 25,
bg_color = None,
corner_radius: int = 10,
border_color: str | tuple[str, str] = "grey50",
separator_color: str | tuple[str, str] = ["grey80","grey20"],
text_color: str | tuple[str, str] = ["black","white"],
fg_color: str | tuple[str, str] = "transparent",
hover_color: str | tuple[str, str] = ["grey75","grey25"],
font: customtkinter.CTkFont = ("helvetica", 12),
padx: int = 3,
pady: int = 3,
**kwargs):

if widget.master.winfo_name().startswith("!ctktitlemenu"):
widget.master.master.bind("<ButtonPress>", self._checkIfMouseLeft, add="+")
master = widget.master if master is None else master
widget.master.menu.append(self)

elif widget.master.winfo_name().startswith("!ctkmenubar"):
widget.winfo_toplevel().bind("<ButtonPress>", self._checkIfMouseLeft, add="+")
master = widget.master.master if master is None else master
widget.master.menu.append(self)
else:
widget.winfo_toplevel().bind("<ButtonPress>", self._checkIfMouseLeft, add="+")
master = widget.master if master is None else master

super().__init__(
master=master,
border_width=border_width,
fg_color=bg_color,
border_color=border_color,
corner_radius=corner_radius,
**kwargs)

self.border_color = border_color
self.border_width = border_width
self.bg_color = bg_color
self.corner_radius = corner_radius
self.menu_seed_object = widget
self.master = master
self.menu_seed_object.configure(command=self.toggleShow)
self.fg_color = fg_color
self.text_color = text_color
self.hover_color = hover_color
self.font = font
self.height = height
self.width = width
self.padx = padx
self.pady = pady
self.separator_color = separator_color
self._options_list: list[_CDMOptionButton | _CDMSubmenuButton] = []

def selectOption(self, command) -> None:
self._hideAllMenus()
if command:
command()

def dummy():
pass

def add_option(self, option: str, command: Callable=dummy, **kwargs) -> None:
optionButton = _CDMOptionButton(
self,
width = self.width,
height = self.height,
text=option,
anchor="w",
text_color=self.text_color,
command=partial(self.selectOption, command), **kwargs)

optionButton.setParentMenu(self)
self._options_list.append(optionButton)
self._configureButton(optionButton)

optionButton.pack(
side="top",
fill="both",
expand=True,
padx=3+(self.corner_radius/5),
pady=3+(self.corner_radius/5))

def add_submenu(self, submenu_name: str, **kwargs) -> "CustomDropdownMenu":
submenuButtonSeed = _CDMSubmenuButton(self, text=submenu_name, anchor="w",
text_color=self.text_color,
width=self.width, height=self.height, **kwargs)
submenuButtonSeed.setParentMenu(self)
self._options_list.append(submenuButtonSeed)
self._configureButton(submenuButtonSeed)

submenu = CustomDropdownMenu(
master=self.master,
height=self.height,
width=self.width,
widget=submenuButtonSeed,
fg_color=self.fg_color,
bg_color=self.bg_color,
hover_color=self.hover_color,
corner_radius=self.corner_radius,
border_width=self.border_width,
border_color=self.border_color,
separator_color=self.separator_color,
text_color=self.text_color,
font=self.font)

submenuButtonSeed.setSubmenu(submenu=submenu)
submenuButtonSeed.configure(command=submenu.toggleShow)

submenuButtonSeed.bind("<Enter>", lambda e: self.after(500, lambda: submenu._show_submenu(self)))
submenuButtonSeed.pack(
side="top",
fill="both",
expand=True,
padx=3+(self.corner_radius/5),
pady=3+(self.corner_radius/5))
return submenu

def _show_submenu(self, parent) ->None:
subMenus = parent._getSubMenus()
for i in subMenus:
i._hide()
self._show()

def add_separator(self) -> None:
separator = customtkinter.CTkFrame(
master=self,
height=2,
width=self.width,
fg_color=self.separator_color,
border_width=0
)
separator.pack(
side="top",
fill="x",
expand=True,
)

def _show(self, *args, **kwargs) -> None:
if isinstance(self.menu_seed_object, _CDMSubmenuButton):
self.place(
in_=self.menu_seed_object.parent_menu,
x=self.menu_seed_object.winfo_x() + self.menu_seed_object.winfo_width() + self.padx +1,
y=self.menu_seed_object.winfo_y() - self.pady)
else:
self.place(
x=self.menu_seed_object.winfo_x() + self.padx ,
y=self.menu_seed_object.winfo_y() + self.menu_seed_object.winfo_height() + self.pady)
self.lift()
self.focus()

def _hide(self, *args, **kwargs) -> None:
self.place_forget()

def _hideParentMenus(self, *args, **kwargs) -> None:
if isinstance(self.menu_seed_object, _CDMSubmenuButton):
self.menu_seed_object.parent_menu._hideParentMenus()
self.menu_seed_object.parent_menu._hide()

def _hideChildrenMenus(self, *args, **kwargs) -> None:
if any(isinstance(option, _CDMSubmenuButton) for option in self._options_list):
for option in self._options_list:
if isinstance(option, _CDMSubmenuButton):
option.submenu._hide()

def _hideAllMenus(self, *args, **kwargs) -> None:
self._hideChildrenMenus()
self._hide()
self._hideParentMenus()

def _collapseSiblingSubmenus(self, button: _CDMOptionButton | _CDMSubmenuButton, *args, **kwargs) -> None:
for option in self._options_list:
if option != button and isinstance(option, _CDMSubmenuButton):
option.submenu._hideChildrenMenus()
option.submenu._hide()

def toggleShow(self, *args, **kwargs) -> None:
widget_base = self.menu_seed_object.master.winfo_name()
if widget_base.startswith("!ctktitlemenu") or widget_base.startswith("!ctkmenubar"):
for i in self.menu_seed_object.master.menu:
i._hide()

if not self.winfo_manager():
self._show()
self.lift()
else:
self._hideChildrenMenus()
self._hide()

def _configureButton(self, button: customtkinter.CTkButton) -> None:
button.configure(fg_color="transparent")
if self.fg_color:
button.configure(fg_color=self.fg_color)
if self.hover_color:
button.configure(hover_color=self.hover_color)
if self.font:
button.configure(font=self.font)

button.bind("<Enter>", partial(self._collapseSiblingSubmenus, button))

def _getSubMenus(self) -> list["CustomDropdownMenu"]:
if any(isinstance(option, _CDMSubmenuButton) for option in self._options_list):
subMenusList = list()
for option in self._options_list:
if isinstance(option, _CDMSubmenuButton):
subMenusList.append(option.submenu)
return subMenusList
else:
return []

def _get_coordinates(self, x_root, y_root) -> bool:
return self.winfo_rootx() < x_root < self.winfo_rootx()+self.winfo_width() and \
self.winfo_rooty() < y_root < self.winfo_rooty()+self.winfo_height()

def _checkIfMouseLeft(self, event: tk.Event=None) -> None:
if not self.winfo_viewable():
return
if not self._get_coordinates(event.x_root, event.y_root):
if isinstance(self.menu_seed_object, _CDMSubmenuButton) and not self.menu_seed_object.parent_menu._get_coordinates(event.x_root, event.y_root):
subMenus = self._getSubMenus()
if subMenus == [] or all((not submenu._get_coordinates(event.x_root, event.y_root)) for submenu in subMenus):
self._hideAllMenus()

elif not isinstance(self.menu_seed_object, _CDMSubmenuButton):
subMenus = self._getSubMenus()
if subMenus == [] or all((not submenu._get_coordinates(event.x_root, event.y_root)) for submenu in subMenus):
self._hideAllMenus()
Loading

0 comments on commit 5501d8b

Please sign in to comment.