1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-21 11:27:38 +03:00

Merge pull request from EDCD/enhancement/theme/use-constants-not-magic-numbers

themes: Use defined constants for which theme throughout
This commit is contained in:
Athanasius 2022-12-06 15:46:50 +00:00 committed by GitHub
commit a0887f1410
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 50 deletions

@ -1799,18 +1799,14 @@ class AppWindow(object):
def onenter(self, event=None) -> None:
"""Handle when our window gains focus."""
# TODO: This assumes that 1) transparent is at least 2, 2) there are
# no new themes added after that.
if config.get_int('theme') > 1:
if config.get_int('theme') == theme.THEME_TRANSPARENT:
self.w.attributes("-transparentcolor", '')
self.blank_menubar.grid_remove()
self.theme_menubar.grid(row=0, columnspan=2, sticky=tk.NSEW)
def onleave(self, event=None) -> None:
"""Handle when our window loses focus."""
# TODO: This assumes that 1) transparent is at least 2, 2) there are
# no new themes added after that.
if config.get_int('theme') > 1 and event.widget == self.w:
if config.get_int('theme') == theme.THEME_TRANSPARENT and event.widget == self.w:
self.w.attributes("-transparentcolor", 'grey4')
self.theme_menubar.grid_remove()
self.blank_menubar.grid(row=0, columnspan=2, sticky=tk.NSEW)
@ -1887,7 +1883,7 @@ sys.path: {sys.path}'''
)
if args.reset_ui:
config.set('theme', 0) # 'Default' theme uses ID 0
config.set('theme', theme.THEME_DEFAULT)
config.set('ui_transparency', 100) # 100 is completely opaque
config.delete('font', suppress=True)
config.delete('font_size', suppress=True)

@ -308,6 +308,7 @@ class PreferencesDialog(tk.Toplevel):
button.bind("<Return>", lambda event: self.apply())
self.protocol("WM_DELETE_WINDOW", self._destroy)
# FIXME: Why are these being called when *creating* the Settings window?
# Selectively disable buttons depending on output settings
self.cmdrchanged()
self.themevarchanged()
@ -733,13 +734,14 @@ class PreferencesDialog(tk.Toplevel):
# Appearance theme and language setting
nb.Radiobutton(
# LANG: Label for 'Default' theme radio button
appearance_frame, text=_('Default'), variable=self.theme, value=0, command=self.themevarchanged
appearance_frame, text=_('Default'), variable=self.theme,
value=theme.THEME_DEFAULT, command=self.themevarchanged
).grid(columnspan=3, padx=self.BUTTONX, sticky=tk.W, row=row.get())
# Appearance theme setting
nb.Radiobutton(
# LANG: Label for 'Dark' theme radio button
appearance_frame, text=_('Dark'), variable=self.theme, value=1, command=self.themevarchanged
appearance_frame, text=_('Dark'), variable=self.theme, value=theme.THEME_DARK, command=self.themevarchanged
).grid(columnspan=3, padx=self.BUTTONX, sticky=tk.W, row=row.get())
if sys.platform == 'win32':
@ -748,7 +750,7 @@ class PreferencesDialog(tk.Toplevel):
# LANG: Label for 'Transparent' theme radio button
text=_('Transparent'), # Appearance theme setting
variable=self.theme,
value=2,
value=theme.THEME_TRANSPARENT,
command=self.themevarchanged
).grid(columnspan=3, padx=self.BUTTONX, sticky=tk.W, row=row.get())
@ -1149,7 +1151,12 @@ class PreferencesDialog(tk.Toplevel):
"""Update theme examples."""
self.theme_button_0['foreground'], self.theme_button_1['foreground'] = self.theme_colors
state = tk.NORMAL if self.theme.get() else tk.DISABLED
if self.theme.get() == theme.THEME_DEFAULT:
state = tk.DISABLED # type: ignore
else:
state = tk.NORMAL # type: ignore
self.theme_label_0['state'] = state
self.theme_label_1['state'] = state
self.theme_button_0['state'] = state

129
theme.py

@ -133,14 +133,14 @@ class _Theme(object):
def __init__(self) -> None:
self.active = None # Starts out with no theme
self.minwidth: Optional[int] = None
self.widgets: Dict[tk.Widget, Set] = {}
self.widgets: Dict[tk.Widget | tk.BitmapImage, Set] = {}
self.widgets_pair: List = []
self.defaults: Dict = {}
self.current: Dict = {}
self.default_ui_scale = None # None == not yet known
self.startup_ui_scale = None
def register(self, widget: tk.Widget) -> None: # noqa: CCR001, C901
def register(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, C901
# Note widget and children for later application of a theme. Note if
# the widget has explicit fg or bg attributes.
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
@ -259,24 +259,7 @@ class _Theme(object):
if not config.get_str('dark_highlight'):
config.set('dark_highlight', 'white')
if theme:
# Dark
(r, g, b) = root.winfo_rgb(config.get_str('dark_text'))
self.current = {
'background': 'grey4', # OSX inactive dark titlebar color
'foreground': config.get_str('dark_text'),
'activebackground': config.get_str('dark_text'),
'activeforeground': 'grey4',
'disabledforeground': f'#{int(r/384):02x}{int(g/384):02x}{int(b/384):02x}',
'highlight': config.get_str('dark_highlight'),
# Font only supports Latin 1 / Supplement / Extended, and a
# few General Punctuation and Mathematical Operators
# LANG: Label for commander name in main window
'font': (theme > 1 and not 0x250 < ord(_('Cmdr')[0]) < 0x3000 and
tk_font.Font(family='Euro Caps', size=10, weight=tk_font.NORMAL) or
'TkDefaultFont'),
}
else:
if theme == self.THEME_DEFAULT:
# (Mostly) system colors
style = ttk.Style()
self.current = {
@ -292,9 +275,30 @@ class _Theme(object):
'font': 'TkDefaultFont',
}
# Apply current theme to a widget and its children, and register it for future updates
else: # Dark *or* Transparent
(r, g, b) = root.winfo_rgb(config.get_str('dark_text'))
self.current = {
'background': 'grey4', # OSX inactive dark titlebar color
'foreground': config.get_str('dark_text'),
'activebackground': config.get_str('dark_text'),
'activeforeground': 'grey4',
'disabledforeground': f'#{int(r/384):02x}{int(g/384):02x}{int(b/384):02x}',
'highlight': config.get_str('dark_highlight'),
# Font only supports Latin 1 / Supplement / Extended, and a
# few General Punctuation and Mathematical Operators
# LANG: Label for commander name in main window
'font': (theme > 1 and not 0x250 < ord(_('Cmdr')[0]) < 0x3000 and
tk_font.Font(family='Euro Caps', size=10, weight=tk_font.NORMAL) or
'TkDefaultFont'),
}
def update(self, widget: tk.Widget) -> None:
"""
Apply current theme to a widget and its children.
Also, register it for future updates.
:param widget: Target widget.
"""
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
if not self.current:
return # No need to call this for widgets created in plugin_app()
@ -306,9 +310,19 @@ class _Theme(object):
self._update_widget(child)
# Apply current theme to a single widget
def _update_widget(self, widget: tk.Widget) -> None: # noqa: CCR001, C901
def _update_widget(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, C901
if widget not in self.widgets:
assert_str = f'{widget.winfo_class()} {widget} "{"text" in widget.keys() and widget["text"]}"'
if isinstance(widget, tk.Widget):
w_class = widget.winfo_class()
w_keys: List[str] = widget.keys()
else:
# There is no tk.BitmapImage.winfo_class()
w_class = ''
# There is no tk.BitmapImage.keys()
w_keys = []
assert_str = f'{w_class} {widget} "{"text" in w_keys and widget["text"]}"'
raise AssertionError(assert_str)
attribs: Set = self.widgets.get(widget, set())
@ -375,8 +389,7 @@ class _Theme(object):
# Apply configured theme
def apply(self, root: tk.Tk) -> None: # noqa: CCR001
def apply(self, root: tk.Tk) -> None: # noqa: CCR001, C901
theme = config.get_int('theme')
self._colors(root, theme)
@ -390,27 +403,35 @@ class _Theme(object):
# Switch menus
for pair, gridopts in self.widgets_pair:
for widget in pair:
widget.grid_remove()
if isinstance(widget, tk.Widget):
widget.grid_remove()
if isinstance(pair[0], tk.Menu):
if theme:
if theme == self.THEME_DEFAULT:
root['menu'] = pair[0]
else: # Dark *or* Transparent
root['menu'] = ''
pair[theme].grid(**gridopts)
else:
root['menu'] = pair[0]
else:
pair[theme].grid(**gridopts)
if self.active == theme:
return # Don't need to mess with the window manager
else:
self.active = theme
if sys.platform == 'darwin':
from AppKit import NSAppearance, NSApplication, NSMiniaturizableWindowMask, NSResizableWindowMask
root.update_idletasks() # need main window to be created
appearance = NSAppearance.appearanceNamed_(theme and
'NSAppearanceNameDarkAqua' or
'NSAppearanceNameAqua')
if theme == self.THEME_DEFAULT:
appearance = NSAppearance.appearanceNamed_('NSAppearanceNameAqua')
else: # Dark (Transparent only on win32)
appearance = NSAppearance.appearanceNamed_('NSAppearanceNameDarkAqua')
for window in NSApplication.sharedApplication().windows():
window.setStyleMask_(window.styleMask() & ~(
NSMiniaturizableWindowMask | NSResizableWindowMask)) # disable zoom
@ -426,14 +447,30 @@ class _Theme(object):
GetWindowLongW = ctypes.windll.user32.GetWindowLongW # noqa: N806 # ctypes
SetWindowLongW = ctypes.windll.user32.SetWindowLongW # noqa: N806 # ctypes
root.overrideredirect(theme and True or False)
root.attributes("-transparentcolor", theme > 1 and 'grey4' or '')
# FIXME: Lose the "treat this like a boolean" bullshit
if theme == self.THEME_DEFAULT:
root.overrideredirect(False)
else:
root.overrideredirect(True)
if theme == self.THEME_TRANSPARENT:
root.attributes("-transparentcolor", 'grey4')
else:
root.attributes("-transparentcolor", '')
root.withdraw()
root.update_idletasks() # Size and windows styles get recalculated here
hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX) # disable maximize
SetWindowLongW(hwnd, GWL_EXSTYLE, theme > 1 and WS_EX_APPWINDOW |
WS_EX_LAYERED or WS_EX_APPWINDOW) # Add to taskbar
if theme == self.THEME_TRANSPARENT:
SetWindowLongW(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_LAYERED) # Add to taskbar
else:
SetWindowLongW(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW) # Add to taskbar
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning
@ -446,11 +483,25 @@ class _Theme(object):
children = Window()
nchildren = c_uint()
XQueryTree(dpy, root.winfo_id(), byref(xroot), byref(parent), byref(children), byref(nchildren))
XChangeProperty(dpy, parent, motif_wm_hints_property, motif_wm_hints_property, 32,
PropModeReplace, theme and motif_wm_hints_dark or motif_wm_hints_normal, 5)
if theme == self.THEME_DEFAULT:
wm_hints = motif_wm_hints_normal
else: # Dark *or* Transparent
wm_hints = motif_wm_hints_dark
XChangeProperty(
dpy, parent, motif_wm_hints_property, motif_wm_hints_property, 32, PropModeReplace, wm_hints, 5
)
XFlush(dpy)
else:
root.overrideredirect(theme and 1 or 0)
if theme == self.THEME_DEFAULT:
root.overrideredirect(False)
else: # Dark *or* Transparent
root.overrideredirect(True)
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning