diff --git a/.gitignore b/.gitignore index b6e4761..181c079 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,7 @@ venv/ ENV/ env.bak/ venv.bak/ +venv_vmcompact/ # Spyder project settings .spyderproject diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..280f7c5 --- /dev/null +++ b/__main__.py @@ -0,0 +1,12 @@ +import voicemeeter +import vmcompact + + +if __name__ == "__main__": + kind_id = "banana" + + voicemeeter.launch(kind_id, hide=False) + + with voicemeeter.remote(kind_id) as vmr: + app = vmcompact.connect(kind_id, vmr) + app.mainloop() diff --git a/configs/app.toml b/configs/app.toml new file mode 100644 index 0000000..7e11b7c --- /dev/null +++ b/configs/app.toml @@ -0,0 +1,15 @@ +# load with themes enabled? set the default mode +[theme] +enabled=true +mode="light" +# load in extended mode? if so which orientation +[extends] +extended=true +extends_horizontal=false +# default dimensions for channel label frames +[channel] +width=80 +height=130 +# default submix bus +[submixes] +default=6 diff --git a/configs/vban.toml b/configs/vban.toml new file mode 100644 index 0000000..231acf1 --- /dev/null +++ b/configs/vban.toml @@ -0,0 +1,12 @@ +# example connections +[connection-1] +kind = 'banana' +ip = '' +streamname = 'Command1' +port = 6990 + +[connection-2] +kind = 'potato' +ip = '' +streamname = 'Command1' +port = 6990 diff --git a/profiles/banana/example.toml b/profiles/banana/example.toml new file mode 100644 index 0000000..7201251 --- /dev/null +++ b/profiles/banana/example.toml @@ -0,0 +1,30 @@ +extends = 'blank' +[strip-0] +label = "PhysStrip0" + +[strip-1] +label = "PhysStrip1" + +[strip-2] +label = "PhysStrip2" + +[strip-3] +label = "VirtStrip0" + +[strip-4] +label = "VirtStrip1" + +[bus-0] +label = "PhysBus0" + +[bus-1] +label = "PhysBus1" + +[bus-2] +label = "PhysBus2" + +[bus-3] +label = "VirtBus0" + +[bus-4] +label = "VirtBus1" diff --git a/profiles/basic/example.toml b/profiles/basic/example.toml new file mode 100644 index 0000000..6aa196d --- /dev/null +++ b/profiles/basic/example.toml @@ -0,0 +1,15 @@ +extends = 'blank' +[strip-0] +label = "PhysStrip0" + +[strip-1] +label = "PhysStrip1" + +[strip-2] +label = "VirtStrip0" + +[bus-0] +label = "PhysBus0" + +[bus-1] +label = "PhysBus1" diff --git a/profiles/potato/example.toml b/profiles/potato/example.toml new file mode 100644 index 0000000..af47002 --- /dev/null +++ b/profiles/potato/example.toml @@ -0,0 +1,48 @@ +extends = 'blank' +[strip-0] +label = "PhysStrip0" + +[strip-1] +label = "PhysStrip1" + +[strip-2] +label = "PhysStrip2" + +[strip-3] +label = "PhysStrip3" + +[strip-4] +label = "PhysStrip4" + +[strip-5] +label = "VirtStrip0" + +[strip-6] +label = "VirtStrip1" + +[strip-7] +label = "VirtStrip2" + +[bus-0] +label = "PhysBus0" + +[bus-1] +label = "PhysBus1" + +[bus-2] +label = "PhysBus2" + +[bus-3] +label = "PhysBus3" + +[bus-4] +label = "PhysBus4" + +[bus-5] +label = "VirtBus0" + +[bus-6] +label = "VirtBus1" + +[bus-7] +label = "VirtBus2" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..31d0644 --- /dev/null +++ b/setup.py @@ -0,0 +1,25 @@ +import setuptools + +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setuptools.setup( + name='vmcompact', + version='0.0.1', + author='Onyx and Iris', + author_email='code@onyxandiris.online', + description='Compact Tkinter Voicemeeter Remote App', + long_description=long_description, + long_description_content_type="text/markdown", + url='https://github.com/onyx-and-iris/voicemeeter-compact', + project_urls = { + "Bug Tracker": "https://github.com/onyx-and-iris/voicemeeter-compact/issues" + }, + license='MIT', + packages=['vmcompact'], + install_requires=[ + 'toml', + 'voicemeeter@git+https://github.com/onyx-and-iris/voicemeeter-api-python#egg=voicemeeter', + 'vbancmd@git+https://github.com/onyx-and-iris/vban-cmd-python#egg=vbancmd', + ], +) diff --git a/vmcompact/__init__.py b/vmcompact/__init__.py new file mode 100644 index 0000000..f78dec4 --- /dev/null +++ b/vmcompact/__init__.py @@ -0,0 +1,3 @@ +from .app import connect + +__ALL__ = ["connect"] diff --git a/vmcompact/app.py b/vmcompact/app.py new file mode 100644 index 0000000..f77b89e --- /dev/null +++ b/vmcompact/app.py @@ -0,0 +1,177 @@ +import tkinter as tk +from tkinter import ttk +from typing import NamedTuple +from functools import partial +from pathlib import Path + +from .errors import VMCompactErrors +from .data import _base_vals, _kinds_all +from .channels import ChannelFrame +from .navigation import Navigation +from .menu import Menus +from .banner import Banner +from .configurations import configuration + + +class App(tk.Tk): + """Topmost Level of App""" + + @classmethod + def make(cls, kind: NamedTuple): + """ + Factory function for App + + Returns an App class of a kind + """ + APP_cls = type( + f"Voicemeeter{kind.name}.Compact", + (cls,), + { + "kind": kind, + }, + ) + return APP_cls + + def __init__(self, vmr): + super().__init__() + defaults = { + "theme": { + "mode": "light", + }, + "extends": { + "extended": False, + "extends_horizontal": True, + }, + } + self.configuration = defaults | self.configuration + _base_vals.themes_enabled = self.configuration["theme"]["enabled"] + _base_vals.extends_horizontal = self.configuration["extends"][ + "extends_horizontal" + ] + _base_vals.submixes = self.configuration["submixes"]["default"] + + # create menus + self.menus = Menus(self, vmr) + self.styletable = ttk.Style() + self._vmr = vmr + + # start pdirty watcher + self.upd_pdirty() + + self.resizable(False, False) + if _base_vals.themes_enabled: + self.apply_theme() + self._make_app(self.kind) + + self.drag_id = "" + self.bind("", self.dragging) + + self.iconbitmap(Path.cwd() / "vmcompact" / "img" / "cat.ico") + + @property + def target(self): + """returns the current interface""" + return self._vban if _base_vals.vban_connected else self._vmr + + @property + def pdirty(self): + return self._pdirty + + @pdirty.setter + def pdirty(self, val): + self._pdirty = val + + @property + def configuration(self): + return configuration["app"] + + @configuration.setter + def configuration(self, val): + self.configuration["app"] = val + + @property + def configframes(self): + """returns a tuple of current config frame addresses""" + return tuple( + frame + for frame in self.winfo_children() + if isinstance(frame, ttk.Frame) + and "!stripconfig" in str(frame) + or "!busconfig" in str(frame) + ) + + def apply_theme(self): + _base_vals.using_theme = True + self.tk.call("source", "./vmcompact/sun-valley-theme/sun-valley.tcl") + self.tk.call("set_theme", self.configuration["theme"]["mode"]) + + def _make_app(self, kind, vban=None): + self.title( + f'Voicemeeter{kind.name}.Compact [{"Local" if not vban else "Network"} Connection]' + ) + self._vban = vban + self.kind = kind + + self._make_top_level_frames() + + def _make_top_level_frames(self): + # initialize bus frame variable + self.bus_frame = None + # channel_frame, left aligned + self.channel_frame = ChannelFrame.make_strips(self) + self.channel_frame.grid(row=0, column=0, sticky=(tk.W)) + # separator + self.sep = ttk.Separator(self, orient="vertical") + self.sep.grid(row=0, column=1, sticky=(tk.S, tk.N)) + self.columnconfigure(1, minsize=15) + + # navigation frame + self.nav_frame = Navigation(self) + self.nav_frame.grid(row=0, column=3) + + if self.configuration["extends"]["extended"]: + self.nav_frame.extend.set(True) + self.nav_frame.extend_frame() + + self.banner = Banner(self) + self.banner.grid(row=4, column=0, columnspan=3) + + def _destroy_top_level_frames(self): + [ + frame.destroy() + for frame in self.winfo_children() + if isinstance(frame, ttk.Frame) + ] + + def upd_pdirty(self): + self.after(1, self.upd_pdirty_step) + + def upd_pdirty_step(self): + self.pdirty = self.target.pdirty + self.after(1, self.upd_pdirty_step) + + def dragging(self, event, *args): + if event.widget is self: + if self.drag_id == "": + _base_vals.in_scale_button_1 = True + _base_vals.dragging = True + else: + self.after_cancel(self.drag_id) + self.drag_id = self.after(100, self.stop_drag) + + def stop_drag(self): + _base_vals.dragging = False + _base_vals.in_scale_button_1 = False + self.drag_id = "" + + +_apps = {kind.id: App.make(kind) for kind in _kinds_all} + + +def connect(kind_id: str, vmr) -> App: + """return App of the kind requested""" + try: + VMMIN_cls = _apps[kind_id] + return VMMIN_cls(vmr) + except KeyError: + raise VMCompactErrors(f"Invalid kind: {kind_id}") diff --git a/vmcompact/banner.py b/vmcompact/banner.py new file mode 100644 index 0000000..d9dfb7a --- /dev/null +++ b/vmcompact/banner.py @@ -0,0 +1,37 @@ +import tkinter as tk +from tkinter import ttk + +from .data import _base_vals + + +class Banner(ttk.Frame): + def __init__(self, parent): + super().__init__() + self._parent = parent + self.web = "onyxandiris.online" + self.submix = tk.StringVar() + if self._parent.kind.name == "Potato": + self.submix.set(self.target.bus[_base_vals.submixes].label) + + if self._parent.kind.name == "Potato": + self.label = ttk.Label( + self, + text=f"SUBMIX: {self.submix.get().upper()}", + ) + self.label.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.W, tk.E)) + + self.upd_submix() + + @property + def target(self): + """use the correct interface""" + return self._parent.target + + def upd_submix(self): + self.after(1, self.upd_submix_step) + + def upd_submix_step(self): + if not _base_vals.dragging: + self.submix.set(self.target.bus[_base_vals.submixes].label) + self.label["text"] = f"SUBMIX: {self.submix.get().upper()}" + self.after(100, self.upd_submix_step) diff --git a/vmcompact/channels.py b/vmcompact/channels.py new file mode 100644 index 0000000..21cbe44 --- /dev/null +++ b/vmcompact/channels.py @@ -0,0 +1,376 @@ +import tkinter as tk +from tkinter import ttk +from functools import partial + +from .data import _base_vals +from .config import StripConfig, BusConfig + + +class Channel(ttk.LabelFrame): + """Base class for a single channel""" + + def __init__(self, parent, index, id): + super().__init__(parent) + self._parent = parent + self.index = index + self.id = id + self.s = self._parent._parent.styletable + self.config_frame = None + + self.gain = tk.DoubleVar() + self.level = tk.DoubleVar() + self.mute = tk.BooleanVar() + self.conf = tk.BooleanVar() + + self.sync() + self._make_widgets() + + self.col_row_configure() + self.watch_pdirty() + self.watch_levels() + + @property + def identifier(self): + return self.id + + @property + def target(self): + """use the correct interface""" + return self._parent.target + + def getter(self, param): + if param in dir(self.target): + return getattr(self.target, param) + + def setter(self, param, value): + if param in dir(self.target): + setattr(self.target, param, value) + + def toggle_mute(self, *args): + self.target.mute = self.mute.get() + if not _base_vals.using_theme: + self.s.configure( + f"{self.identifier}Mute{self.index}.TButton", + background=f'{"red" if self.mute.get() else "white"}', + ) + + def reset_gain(self, *args): + self.setter("gain", 0) + self.gain.set(0) + self._parent._parent.nav_frame.info_text.set(0) + + def scale_enter(self, *args): + self._parent._parent.nav_frame.info_text.set(round(self.gain.get(), 1)) + + def scale_leave(self, *args): + self._parent._parent.nav_frame.info_text.set("") + + def scale_press(self, *args): + _base_vals.in_scale_button_1 = True + + def scale_release(self, *args): + _base_vals.in_scale_button_1 = False + + def scale_callback(self, *args): + """callback function for scale widget""" + self.setter("gain", self.gain.get()) + self._parent._parent.nav_frame.info_text.set(round(self.gain.get(), 1)) + + def _make_widgets(self): + """Creates a progressbar, scale, mute button and config button for a single channel""" + # Progress bar + self.pb = ttk.Progressbar( + self, + maximum=100, + orient="vertical", + mode="determinate", + variable=self.level, + ) + self.pb.grid(column=0, row=0) + + # Scale + self.scale = ttk.Scale( + self, + from_=12.0, + to=-60.0, + orient="vertical", + variable=self.gain, + command=self.scale_callback, + length=self._parent.height, + ) + self.scale.grid(column=1, row=0) + self.scale.bind("", self.reset_gain) + self.scale.bind("", self.scale_press) + self.scale.bind("", self.scale_enter) + self.scale.bind("", self.scale_release) + self.scale.bind("", self.scale_leave) + + # Mute button + self.button_mute = ttk.Checkbutton( + self, + text="MUTE", + command=partial(self.toggle_mute, "mute"), + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{self.identifier}Mute{self.index}.TButton'}", + variable=self.mute, + ) + self.button_mute.grid(column=0, row=1, columnspan=2) + + self.button_conf = ttk.Checkbutton( + self, + text="CONFIG", + command=self.open_config, + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{self.identifier}Conf{self.index}.TButton'}", + variable=self.conf, + ) + self.button_conf.grid(column=0, row=2, columnspan=2) + + def watch_pdirty(self): + self.after(1, self.watch_pdirty_step) + + def watch_pdirty_step(self): + """keeps params synced but ensures sliders are responsive""" + if self._parent._parent.pdirty and not _base_vals.in_scale_button_1: + self.sync() + self.after(1, self.watch_pdirty_step) + + def sync(self): + """sync params with voicemeeter""" + retval = self.getter("label") + if len(retval) > 10: + retval = f"{retval[:8]}.." + self.configure(text=retval) + self.gain.set(self.getter("gain")) + self.mute.set(self.getter("mute")) + if not _base_vals.using_theme: + self.s.configure( + f"{self.identifier}Mute{self.index}.TButton", + background=f'{"red" if self.mute.get() else "white"}', + ) + self.s.configure( + f"{self.identifier}Conf{self.index}.TButton", background="white" + ) + + def col_row_configure(self): + self.grid(sticky=(tk.N, tk.S)) + [ + child.grid_configure(padx=1, pady=1, sticky=(tk.W, tk.E)) + for child in self.winfo_children() + if isinstance(child, ttk.Checkbutton) + ] + [ + child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S)) + for child in self.winfo_children() + if isinstance(child, ttk.Progressbar) or isinstance(child, ttk.Scale) + ] + + +class Strip(Channel): + """Concrete class representing a single""" + + def __init__(self, parent, index, id): + super().__init__(parent, index, id) + + @property + def target(self): + """use the correct interface""" + _target = super(Strip, self).target + return getattr(_target, self.identifier)[self.index] + + def open_config(self): + if self.conf.get(): + self.config_frame = StripConfig( + self._parent._parent, + self.index, + self.identifier, + ) + self.config_frame.grid(column=0, row=1, columnspan=4) + self._parent._parent.channel_frame.reset_config_buttons(self) + if self._parent._parent.bus_frame is not None: + self._parent._parent.bus_frame.reset_config_buttons(self) + else: + self.config_frame.destroy() + + if not _base_vals.using_theme: + self.s.configure( + f"{self.identifier}Conf{self.index}.TButton", + background=f'{"yellow" if self.conf.get() else "white"}', + ) + + def watch_levels(self): + self.after(1, self.watch_levels_step) + + def watch_levels_step(self): + if not _base_vals.dragging: + vals = self.target.levels.prefader + val = vals[0] if vals[0] > vals[1] else vals[0] + self.level.set(val) + self.level.set( + (0 if self.mute.get() else 100 + (val - 18) + self.gain.get()) + ) + self.after( + 25 if not _base_vals.in_scale_button_1 else 100, self.watch_levels_step + ) + + +class Bus(Channel): + """Concrete bus class representing a single bus""" + + def __init__(self, parent, index, id): + super().__init__(parent, index, id) + + @property + def target(self): + """use the correct interface""" + _target = super(Bus, self).target + return getattr(_target, self.identifier)[self.index] + + def open_config(self): + if self.conf.get(): + self.config_frame = BusConfig( + self._parent._parent, + self.index, + self.identifier, + ) + if _base_vals.extends_horizontal: + self.config_frame.grid(column=0, row=1, columnspan=3) + else: + self.config_frame.grid(column=0, row=3, columnspan=3) + self._parent._parent.channel_frame.reset_config_buttons(self) + self._parent._parent.bus_frame.reset_config_buttons(self) + else: + self.config_frame.destroy() + + if not _base_vals.using_theme: + self.s.configure( + f"{self.identifier}Conf{self.index}.TButton", + background=f'{"yellow" if self.conf.get() else "white"}', + ) + + def watch_levels(self): + self.after(1, self.watch_levels_step) + + def watch_levels_step(self): + if not _base_vals.dragging: + vals = self.target.levels.all + val = vals[0] if vals[0] > vals[1] else vals[0] + self.level.set(val) + self.level.set( + (0 if self.mute.get() else 100 + (val - 18) + self.gain.get()) + ) + self.after( + 25 if not _base_vals.in_scale_button_1 else 100, self.watch_levels_step + ) + + +class ChannelFrame(ttk.Frame): + @classmethod + def make_strips(cls, parent): + return cls(parent, is_strip=True) + + @classmethod + def make_buses(cls, parent): + return cls(parent, is_strip=False) + + def __init__(self, parent, is_strip: bool = True): + super().__init__(parent) + self._parent = parent + self._is_strip = is_strip + self.phys_in, self.virt_in = parent.kind.ins + self.phys_out, self.virt_out = parent.kind.outs + + defaults = { + "width": 80, + "height": 150, + } + self.configuration = defaults | self.configuration + self.width = self.configuration["width"] + self.height = self.configuration["height"] + + self.watch_pdirty() + + # create labelframes + if is_strip: + self.strips = [ + Strip(self, i, self.identifier) + for i in range(self.phys_in + self.virt_in) + ] + else: + self.buses = [ + Bus(self, i, self.identifier) + for i in range(self.phys_out + self.virt_out) + ] + + # position label frames. destroy any without label text + self.labelframes = self.strips if is_strip else self.buses + + self.col_row_configure() + + for i, labelframe in enumerate(self.labelframes): + labelframe.grid(row=0, column=i) + if not labelframe.cget("text"): + self.columnconfigure(i, minsize=0) + labelframe.grid_remove() + + @property + def target(self): + """returns the current interface""" + return self._parent.target + + @property + def configuration(self): + return self._parent.configuration["channel"] + + @configuration.setter + def configuration(self, val): + self._parent.configuration["channel"] = val + + @property + def identifier(self): + return "strip" if self._is_strip else "bus" + + def reset_config_buttons(self, current): + if not _base_vals.using_theme: + [ + labelframe.s.configure( + f"{labelframe.identifier}Conf{labelframe.index}.TButton", + background="white", + ) + for labelframe in self.labelframes + if labelframe is not None + ] + [ + labelframe.conf.set(False) + for labelframe in self.labelframes + if labelframe is not None and labelframe != current + ] + [ + labelframe.config_frame.destroy() + for labelframe in self.labelframes + if labelframe is not None + and labelframe.config_frame + and labelframe != current + ] + + def col_row_configure(self): + [ + self.columnconfigure(i, minsize=self.width) + for i, _ in enumerate(self.labelframes) + ] + [self.rowconfigure(0, minsize=130) for i, _ in enumerate(self.labelframes)] + + def watch_pdirty(self): + self.after(1, self.watch_pdirty_step) + + def watch_pdirty_step(self): + if self._parent.pdirty: + self.watch_labels() + self.after(1, self.watch_pdirty_step) + + def watch_labels(self): + for i, labelframe in enumerate(self.labelframes): + if not labelframe.getter("label"): + self.columnconfigure(i, minsize=0) + labelframe.grid_remove() + else: + self.columnconfigure(i, minsize=self.width) + labelframe.grid() diff --git a/vmcompact/config.py b/vmcompact/config.py new file mode 100644 index 0000000..4a0cea0 --- /dev/null +++ b/vmcompact/config.py @@ -0,0 +1,458 @@ +import tkinter as tk +from tkinter import ttk +from functools import partial + +from .data import _base_vals + + +class Config(ttk.Frame): + def __init__(self, parent, index, _id): + super().__init__(parent) + self._parent = parent + self.index = index + self.id = _id + self.s = parent.styletable + + self.phys_in, self.virt_in = parent.kind.ins + self.phys_out, self.virt_out = parent.kind.outs + + self.watch_pdirty() + + @property + def identifier(self): + return self.id + + @property + def target(self): + """returns the current interface""" + return self._parent.target + + def getter(self, param): + if param in dir(self.target): + return getattr(self.target, param) + + def setter(self, param, value): + if param in dir(self.target): + setattr(self.target, param, value) + + def scale_enter(self, *args): + _base_vals.in_scale_button_1 = True + + def scale_leave(self, *args): + _base_vals.in_scale_button_1 = False + self._parent.nav_frame.info_text.set("") + + def scale_callback(self, param, *args): + """callback function for scale widget""" + val = self.slider_vars[self.slider_params.index(param)].get() + self.setter(param, val) + self._parent.nav_frame.info_text.set(round(val, 1)) + + def reset_scale(self, param, val, *args): + self.setter(param, val) + self.slider_vars[self.slider_params.index(param)].set(val) + + def col_row_configure(self): + [ + child.grid_configure(padx=1, pady=1, sticky=(tk.W, tk.E)) + for child in self.winfo_children() + if isinstance(child, ttk.Checkbutton) + ] + self.grid(sticky=(tk.W)) + + def watch_pdirty(self): + self.after(1, self.watch_pdirty_step) + + def watch_pdirty_step(self): + """keeps params synced but ensures sliders are responsive""" + if self._parent.pdirty and not _base_vals.in_scale_button_1: + self.sync() + self.after(1, self.watch_pdirty_step) + + +class StripConfig(Config): + def __init__(self, parent, index, _id): + super().__init__(parent, index, _id) + + # create parameter variables + if self._parent.kind.name == "Basic": + self.slider_params = ("audibility",) + self.slider_vars = (tk.DoubleVar(),) + else: + self.slider_params = ("comp", "gate", "limit") + self.slider_vars = [ + tk.DoubleVar() for i, _ in enumerate(self.slider_params) + ] + + self.phys_out_params = [f"A{i+1}" for i in range(self.phys_out)] + self.phys_out_params_vars = [ + tk.BooleanVar() for i, _ in enumerate(self.phys_out_params) + ] + + self.virt_out_params = [f"B{i+1}" for i in range(self.virt_out)] + self.virt_out_params_vars = [ + tk.BooleanVar() for i, _ in enumerate(self.virt_out_params) + ] + + self.params = ("mono", "solo") + self.param_vars = list(tk.BooleanVar() for i, _ in enumerate(self.params)) + + self.make_row0() + self.make_row1() + self.make_row2() + + # sync all parameters + self.sync() + self.sync_sliders() + + self.col_row_configure() + + @property + def target(self): + """use the correct interface""" + _target = super(StripConfig, self).target + return getattr(_target, self.identifier)[self.index] + + def make_row0(self): + # Create sliders + if self.index < self.phys_in: + if self._parent.kind.name == "Basic": + # audibility + aud_label = ttk.Label(self, text="Audibility") + aud_scale = ttk.Scale( + self, + from_=0.0, + to=10.0, + orient="horizontal", + length=_base_vals.level_width, + variable=self.slider_vars[self.slider_params.index("audibility")], + command=partial(self.scale_callback, "audibility"), + ) + aud_scale.bind( + "", partial(self.reset_scale, "audibility", 0) + ) + aud_scale.bind("", self.scale_enter) + aud_scale.bind("", self.scale_leave) + + aud_label.grid(column=0, row=0) + aud_scale.grid(column=1, row=0) + else: + # comp + comp_label = ttk.Label(self, text="Comp") + comp_scale = ttk.Scale( + self, + from_=0.0, + to=10.0, + orient="horizontal", + length=_base_vals.level_width, + variable=self.slider_vars[self.slider_params.index("comp")], + command=partial(self.scale_callback, "comp"), + ) + comp_scale.bind( + "", partial(self.reset_scale, "comp", 0) + ) + comp_scale.bind("", self.scale_enter) + comp_scale.bind("", self.scale_leave) + + # gate + gate_label = ttk.Label(self, text="Gate") + gate_scale = ttk.Scale( + self, + from_=0.0, + to=10.0, + orient="horizontal", + length=_base_vals.level_width, + variable=self.slider_vars[self.slider_params.index("gate")], + command=partial(self.scale_callback, "gate"), + ) + gate_scale.bind( + "", partial(self.reset_scale, "gate", 0) + ) + gate_scale.bind("", self.scale_enter) + gate_scale.bind("", self.scale_leave) + + # limit + limit_label = ttk.Label(self, text="Limit") + limit_scale = ttk.Scale( + self, + from_=-40, + to=12, + orient="horizontal", + length=_base_vals.level_width, + variable=self.slider_vars[self.slider_params.index("limit")], + command=partial(self.scale_callback, "limit"), + ) + limit_scale.bind( + "", partial(self.reset_scale, "limit", 12) + ) + limit_scale.bind("", self.scale_enter) + limit_scale.bind("", self.scale_leave) + + # Position sliders + comp_label.grid(column=0, row=0) + comp_scale.grid(column=1, row=0) + gate_label.grid(column=2, row=0) + gate_scale.grid(column=3, row=0) + limit_label.grid(column=4, row=0) + limit_scale.grid(column=5, row=0) + + def make_row1(self): + # create buttons + self.a_buttons = [ + ttk.Checkbutton( + self, + text=param, + command=partial(self.toggle_a, param), + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}", + variable=self.phys_out_params_vars[self.phys_out_params.index(param)], + ) + for param in self.phys_out_params + ] + self.b_buttons = [ + ttk.Checkbutton( + self, + text=param, + command=partial(self.toggle_b, param), + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}", + variable=self.virt_out_params_vars[self.virt_out_params.index(param)], + ) + for param in self.virt_out_params + ] + + # set button positions + [ + button.grid( + column=self.a_buttons.index(button), + row=1, + ) + for button in self.a_buttons + ] + [ + button.grid( + column=len(self.a_buttons) + self.b_buttons.index(button), + row=1, + ) + for button in self.b_buttons + ] + + def toggle_a(self, param): + val = self.phys_out_params_vars[self.phys_out_params.index(param)].get() + self.setter(param, val) + if not _base_vals.using_theme: + self.s.configure( + f"{param}.TButton", background=f'{"green" if val else "white"}' + ) + + def toggle_b(self, param): + val = self.virt_out_params_vars[self.virt_out_params.index(param)].get() + self.setter(param, val) + if not _base_vals.using_theme: + self.s.configure( + f"{param}.TButton", background=f'{"green" if val else "white"}' + ) + + def make_row2(self): + if self._parent.kind.name in ("Banana", "Potato"): + if self.index == self.phys_in: + self.params = list(map(lambda x: x.replace("mono", "mc"), self.params)) + if self._parent.kind.name == "Banana": + pass + # karaoke modes not in RT Packet yet. May implement in future + """ + if self.index == self.phys_in + 1: + self.params = list( + map(lambda x: x.replace("mono", "k"), self.params) + ) + self.param_vars[self.params.index("k")] = tk.IntVar + """ + else: + if self.index == self.phys_in + self.virt_in - 1: + self.params = list( + map(lambda x: x.replace("mono", "mc"), self.params) + ) + + param_buttons = [ + ttk.Checkbutton( + self, + text=param, + command=partial(self.toggle_p, param), + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}", + variable=self.param_vars[self.params.index(param)], + ) + for param in self.params + ] + [ + button.grid( + column=param_buttons.index(button), + row=2, + ) + for button in param_buttons + ] + + def toggle_p(self, param): + val = self.param_vars[self.params.index(param)].get() + self.setter(param, val) + if not _base_vals.using_theme: + self.s.configure( + f"{param}.TButton", background=f'{"green" if val else "white"}' + ) + + def sync(self): + [ + self.phys_out_params_vars[self.phys_out_params.index(param)].set( + self.getter(param) + ) + for param in self.phys_out_params + ] + [ + self.virt_out_params_vars[self.virt_out_params.index(param)].set( + self.getter(param) + ) + for param in self.virt_out_params + ] + [ + self.param_vars[self.params.index(param)].set(self.getter(param)) + for param in self.params + ] + if not _base_vals.using_theme: + [ + self.s.configure( + f"{param}.TButton", + background=f'{"green" if self.phys_out_params_vars[self.phys_out_params.index(param)].get() else "white"}', + ) + for param in self.phys_out_params + ] + [ + self.s.configure( + f"{param}.TButton", + background=f'{"green" if self.virt_out_params_vars[self.virt_out_params.index(param)].get() else "white"}', + ) + for param in self.virt_out_params + ] + [ + self.s.configure( + f"{param}.TButton", + background=f'{"green" if self.param_vars[self.params.index(param)].get() else "white"}', + ) + for param in self.params + ] + + def sync_sliders(self): + [ + self.slider_vars[self.slider_params.index(param)].set(self.getter(param)) + for param in self.slider_params + ] + + def col_row_configure(self): + super(StripConfig, self).col_row_configure() + [ + self.columnconfigure(i, minsize=80) + for i in range(self.phys_out + self.virt_out) + ] + + +class BusConfig(Config): + def __init__(self, parent, index, _id): + super().__init__(parent, index, _id) + # fmt: off + # create parameter variables + self.bus_modes = ( + "normal", "Amix", "Bmix", "Repeat", "Composite", "TVMix", "UpMix21", + "UpMix41", "UpMix61", "CenterOnly", "LFEOnly", "RearOnly", + ) + # fmt: on + self.params = ("mono", "eq", "eq_ab") + self.param_vars = [tk.BooleanVar() for i, _ in enumerate(self.params)] + + self.make_row0() + self.make_row1() + + # sync all parameters + self.sync() + + self.col_row_configure() + + @property + def target(self): + """returns the current interface""" + _target = super(BusConfig, self).target + return getattr(_target, self.identifier)[self.index] + + def make_row0(self): + self._cur_bus_mode = tk.StringVar() + self._cur_bus_mode.set(f"Bus Mode: {self.bus_modes[self.get_current_index()]}") + self.busmode_button = ttk.Button(self, textvariable=self._cur_bus_mode) + self.busmode_button.grid(column=0, row=0, columnspan=2, sticky=(tk.W)) + self.busmode_button.bind("", self.rotate_bus_modes_right) + self.busmode_button.bind("", self.rotate_bus_modes_left) + + def get_current_index(self): + for mode in self.bus_modes: + if getattr(self.target.mode, mode.lower()): + return self.bus_modes.index(mode) + + def rotate_bus_modes_right(self, *args): + current_index = self.get_current_index() + if current_index + 1 < len(self.bus_modes): + next = self.bus_modes[current_index + 1] + else: + next = self.bus_modes[0] + setattr(self.target.mode, next.lower(), True) + self._cur_bus_mode.set(f"Bus Mode: {next}") + + def rotate_bus_modes_left(self, *args): + current_index = self.get_current_index() + if current_index == 0: + next = self.bus_modes[-1] + else: + next = self.bus_modes[current_index - 1] + setattr(self.target.mode, next.lower(), True) + self._cur_bus_mode.set(f"Bus Mode: {next}") + + def make_row1(self): + param_buttons = [ + ttk.Checkbutton( + self, + text=param, + command=partial(self.toggle_p, param), + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'{param}.TButton'}", + variable=self.param_vars[self.params.index(param)], + ) + for param in self.params + ] + [ + button.grid( + column=param_buttons.index(button), + row=1, + ) + for button in param_buttons + ] + + def toggle_p(self, param): + val = self.param_vars[self.params.index(param)].get() + self.setter(param, val) + if not _base_vals.using_theme: + self.s.configure( + f"{param}.TButton", background=f'{"green" if val else "white"}' + ) + + def col_row_configure(self): + super(BusConfig, self).col_row_configure() + [ + self.columnconfigure(i, minsize=80) + for i in range(self.phys_out + self.virt_out) + ] + + def sync(self): + [ + self.param_vars[self.params.index(param)].set(self.getter(param)) + for param in self.params + ] + if not _base_vals.using_theme: + [ + self.s.configure( + f"{param}.TButton", + background=f'{"green" if self.param_vars[self.params.index(param)].get() else "white"}', + ) + for param in self.params + ] diff --git a/vmcompact/configurations.py b/vmcompact/configurations.py new file mode 100644 index 0000000..d064fba --- /dev/null +++ b/vmcompact/configurations.py @@ -0,0 +1,20 @@ +import toml +from pathlib import Path + +configuration = {} + +config_path = [Path.cwd() / "configs"] +for path in config_path: + if path.is_dir(): + filenames = list(path.glob("*.toml")) + configs = {} + for filename in filenames: + name = filename.with_suffix("").stem + try: + configs[name] = toml.load(filename) + except toml.TomlDecodeError: + print(f"Invalid TOML profile: configs/{filename.stem}") + + for name, cfg in configs.items(): + print(f"Loaded profile configs/{name}") + configuration[name] = cfg diff --git a/vmcompact/data.py b/vmcompact/data.py new file mode 100644 index 0000000..200ef78 --- /dev/null +++ b/vmcompact/data.py @@ -0,0 +1,35 @@ +from dataclasses import dataclass +from voicemeeter import kinds + + +@dataclass +class BaseValues: + level_height: int = 100 + level_width: int = 80 + + # are we dragging a scale with mouse 1 + in_scale_button_1: bool = False + # are we dragging main window with mouse 1 + dragging: bool = False + # direction the gui extends + extends_horizontal: bool = True + # a vban connection established + vban_connected: bool = False + # are themes enabled + themes_enabled: bool = True + # are we using a theme + using_theme: bool = False + # bus assigned as current submix + submixes: int = 0 + + +_base_vals = BaseValues() + + +_kinds = {kind.id: kind for kind in kinds.all} + +_kinds_all = _kinds.values() + + +def kind_get(kind_id): + return _kinds[kind_id] diff --git a/vmcompact/errors.py b/vmcompact/errors.py new file mode 100644 index 0000000..abd1b38 --- /dev/null +++ b/vmcompact/errors.py @@ -0,0 +1,4 @@ +class VMCompactErrors(Exception): + """Base classs for VMCompact Errors""" + + pass diff --git a/vmcompact/gainlayer.py b/vmcompact/gainlayer.py new file mode 100644 index 0000000..88422e0 --- /dev/null +++ b/vmcompact/gainlayer.py @@ -0,0 +1,244 @@ +import tkinter as tk +from tkinter import ttk, messagebox as msg +from functools import partial + +from .data import _base_vals + + +class GainLayer(ttk.LabelFrame): + """Concrete class representing a single gainlayer""" + + def __init__(self, parent, index, j): + super().__init__(parent) + self._parent = parent + self.index = index + self.j = j + self.gain = tk.DoubleVar() + self.level = tk.DoubleVar() + self.on = tk.BooleanVar() + self.s = self._parent._parent.styletable + + self.sync() + self._make_widgets() + + self.col_row_configure() + self.watch_pdirty() + self.watch_levels() + + @property + def target(self): + """returns the current interface""" + _target = self._parent.target + return _target.strip[self.index].gainlayer[self.j] + + def getter(self, param): + if param in dir(self.target): + return getattr(self.target, param) + + def setter(self, param, value): + if param in dir(self.target): + setattr(self.target, param, value) + + def reset_gain(self, *args): + self.setter("gain", 0) + self.gain.set(0) + self._parent._parent.nav_frame.info_text.set(0) + + def scale_enter(self, *args): + self._parent._parent.nav_frame.info_text.set(round(self.gain.get(), 1)) + + def scale_leave(self, *args): + self._parent._parent.nav_frame.info_text.set("") + + def scale_press(self, *args): + _base_vals.in_scale_button_1 = True + + def scale_release(self, *args): + _base_vals.in_scale_button_1 = False + + def scale_callback(self, *args): + """callback function for scale widget""" + self.setter("gain", self.gain.get()) + self._parent._parent.nav_frame.info_text.set(round(self.gain.get(), 1)) + + def set_on(self): + """enables a gainlayer. sets its button colour""" + setattr( + self._parent.target.strip[self.index], + self._parent.buses[self.j], + self.on.get(), + ) + if not _base_vals.using_theme: + self.s.configure( + f"On.TButton", + background=f'{"green" if self.on.get() else "white"}', + ) + + def _make_widgets(self): + """Creates a progressbar, scale, on button and config button for a single channel""" + # Progress bar + self.pb = ttk.Progressbar( + self, + maximum=100, + orient="vertical", + mode="determinate", + variable=self.level, + ) + self.pb.grid(column=0, row=0) + + # Scale + self.scale = ttk.Scale( + self, + from_=12.0, + to=-60.0, + orient="vertical", + variable=self.gain, + command=self.scale_callback, + length=self._parent.height, + ) + self.scale.grid(column=1, row=0) + self.scale.bind("", self.reset_gain) + self.scale.bind("", self.scale_press) + self.scale.bind("", self.scale_enter) + self.scale.bind("", self.scale_release) + self.scale.bind("", self.scale_leave) + + # On button + self.button_on = ttk.Checkbutton( + self, + text="ON", + command=self.set_on, + style=f"{'Toggle.TButton' if _base_vals.using_theme else 'On.TButton'}", + variable=self.on, + ) + self.button_on.grid(column=0, row=1, columnspan=2) + + def col_row_configure(self): + [ + child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S, tk.W, tk.E)) + for child in self.winfo_children() + if isinstance(child, ttk.Checkbutton) + ] + [ + child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S)) + for child in self.winfo_children() + if isinstance(child, ttk.Progressbar) or isinstance(child, ttk.Scale) + ] + self.columnconfigure(0, minsize=36) + self.columnconfigure(1, minsize=36) + self.rowconfigure(1, minsize=70) + + def watch_pdirty(self): + self.after(1, self.watch_pdirty_step) + + def watch_pdirty_step(self): + """keeps params synced but ensures sliders are responsive""" + if self._parent._parent.pdirty and not _base_vals.in_scale_button_1: + self.sync() + self.after(1, self.watch_pdirty_step) + + def sync(self): + """sync params with voicemeeter""" + retval = self.getter("label") + if len(retval) > 10: + retval = f"{retval[:8]}.." + self.configure(text=retval) + self.gain.set(self.getter("gain")) + self.on.set( + getattr( + self._parent.target.strip[self.index], + self._parent.buses[self.j], + ) + ) + + def watch_levels(self): + self.after(1, self.watch_levels_step) + + def watch_levels_step(self): + if not _base_vals.dragging: + vals = self._parent.target.strip[self.index].levels.prefader + val = vals[0] if vals[0] > vals[1] else vals[0] + self.level.set(val) + self.level.set( + ( + 0 + if self._parent._parent.channel_frame.strips[self.index].mute.get() + else 100 + (val - 18) + self.gain.get() + ) + ) + self.after( + 25 if not _base_vals.in_scale_button_1 else 100, self.watch_levels_step + ) + + +class SubMixFrame(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self._parent = parent + self.phys_out, self.virt_out = parent.kind.outs + self.buses = tuple(f"A{i+1}" for i in range(self.phys_out)) + tuple( + f"B{i+1}" for i in range(self.virt_out) + ) + defaults = { + "width": 80, + "height": 150, + } + self.configuration = defaults | self.configuration + self.width = self.configuration["width"] + self.height = self.configuration["height"] + + self.gainlayers = [ + GainLayer(self, index, _base_vals.submixes) for index in range(8) + ] + [ + gainlayer.grid(row=0, column=self.gainlayers.index(gainlayer)) + for gainlayer in self.gainlayers + ] + + self.col_row_configure() + + # destroy any without label text + for i, gainlayer in enumerate(self.gainlayers): + gainlayer.grid(row=0, column=i) + if not gainlayer.cget("text"): + self.columnconfigure(i, minsize=0) + gainlayer.grid_remove() + + self.watch_pdirty() + + @property + def target(self): + """returns the current interface""" + return self._parent.target + + @property + def configuration(self): + return self._parent.configuration["channel"] + + @configuration.setter + def configuration(self, val): + self._parent.configuration["channel"] = val + + def col_row_configure(self): + [ + self.columnconfigure(i, minsize=self.width) + for i, _ in enumerate(self.gainlayers) + ] + [self.rowconfigure(0, minsize=130) for i, _ in enumerate(self.gainlayers)] + + def watch_pdirty(self): + self.after(1, self.watch_pdirty_step) + + def watch_pdirty_step(self): + if self._parent.pdirty: + self.watch_labels() + self.after(1, self.watch_pdirty_step) + + def watch_labels(self): + for i, gainlayer in enumerate(self.gainlayers): + if not self.target.strip[gainlayer.index].label: + self.columnconfigure(i, minsize=0) + gainlayer.grid_remove() + else: + self.columnconfigure(i, minsize=80) + gainlayer.grid() diff --git a/vmcompact/img/cat.ico b/vmcompact/img/cat.ico new file mode 100644 index 0000000..254c6e1 Binary files /dev/null and b/vmcompact/img/cat.ico differ diff --git a/vmcompact/menu.py b/vmcompact/menu.py new file mode 100644 index 0000000..c8d762c --- /dev/null +++ b/vmcompact/menu.py @@ -0,0 +1,316 @@ +import tkinter as tk +from tkinter import ttk, messagebox +from functools import partial +import webbrowser + +import vbancmd + +from .configurations import configuration +from .data import _base_vals, kind_get +from .gainlayer import SubMixFrame + + +class Menus(tk.Menu): + def __init__(self, parent, vmr): + super().__init__() + self._parent = parent + self._vmr = vmr + self.vban_conns = [None for i, _ in enumerate(self.configuration_vban)] + self.menubar = tk.Menu(self, tearoff=0) + parent["menu"] = self.menubar + self._is_topmost = tk.BooleanVar() + self._selected_bus = list(tk.BooleanVar() for _ in range(8)) + + # voicemeeter menu + self.menu_voicemeeter = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(menu=self.menu_voicemeeter, label="Voicemeeter") + self.menu_voicemeeter.add_checkbutton( + label="Always On Top", + onvalue=1, + offvalue=0, + variable=self._is_topmost, + command=self.always_on_top, + ) + self.menu_voicemeeter.add_separator() + self.menu_voicemeeter.add_command( + label="Show", + underline=0, + command=partial(self.action_invoke_voicemeeter, "show"), + ) + self.menu_voicemeeter.add_command( + label="Hide", + underline=0, + command=partial(self.action_invoke_voicemeeter, "hide"), + ) + self.menu_voicemeeter.add_command( + label="Restart", + underline=0, + command=partial(self.action_invoke_voicemeeter, "restart"), + ) + self.menu_voicemeeter.add_command( + label="Shutdown", + underline=0, + command=partial(self.action_invoke_voicemeeter, "shutdown"), + ) + self.menu_voicemeeter.add_separator() + self.menu_lock = tk.Menu(self.menu_voicemeeter, tearoff=0) + self.menu_voicemeeter.add_cascade( + menu=self.menu_lock, label="GUI Lock", underline=0 + ) + self.menu_lock.add_command( + label="Lock", command=partial(self.action_set_voicemeeter, "lock") + ) + self.menu_lock.add_command( + label="Unlock", command=partial(self.action_set_voicemeeter, "lock", False) + ) + + # profiles menu + menu_profiles = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(menu=menu_profiles, label="Profiles") + self.menu_profiles_load = tk.Menu(menu_profiles, tearoff=0) + menu_profiles.add_cascade(menu=self.menu_profiles_load, label="Load profile") + defaults = {"base", "blank"} + if len(vmr.profiles) > len(defaults) and all( + key in vmr.profiles for key in defaults + ): + [ + self.menu_profiles_load.add_command( + label=profile, command=partial(self.load_profile, profile) + ) + for profile in vmr.profiles.keys() + if profile not in defaults + ] + else: + menu_profiles.entryconfig(0, state="disabled") + menu_profiles.add_separator() + menu_profiles.add_command(label="Reset to defaults", command=self.load_defaults) + + # vban connect menu + self.menu_vban = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(menu=self.menu_vban, label="VBAN Connect") + for i, _ in enumerate(self.configuration_vban): + setattr(self, f"menu_vban_{i+1}", tk.Menu(self.menu_vban, tearoff=0)) + target_menu = getattr(self, f"menu_vban_{i+1}") + self.menu_vban.add_cascade( + menu=target_menu, label=f"VBAN Connect #{i+1}", underline=0 + ) + target_menu.add_command( + label="Connect", command=partial(self.vban_connect, i) + ) + target_menu.add_command( + label="Disconnect", command=partial(self.vban_disconnect, i) + ) + target_menu.entryconfig(1, state="disabled") + + # extends menu + self.menu_extends = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(menu=self.menu_extends, label="Extends") + self.menu_extends.add_command( + label="horizontal", + underline=0, + command=partial(self.switch_orientation, extends_horizontal=True), + ) + self.menu_extends.add_command( + label="vertical", + underline=0, + command=partial(self.switch_orientation, extends_horizontal=False), + ) + self.menu_extends.entryconfig( + 0 if _base_vals.extends_horizontal else 1, state="disabled" + ) + + # submixes menu + # here we build menu regardless of kind but disable if not Potato + buses = tuple(f"A{i+1}" for i in range(5)) + tuple(f"B{i+1}" for i in range(3)) + self.menu_submixes = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(menu=self.menu_submixes, label="Submixes") + [ + self.menu_submixes.add_checkbutton( + label=f"Bus {buses[i]}", + underline=0, + onvalue=1, + offvalue=0, + variable=self._selected_bus[i], + command=partial(self.set_submix, i), + ) + for i in range(8) + ] + self._selected_bus[_base_vals.submixes].set(True) + if self._parent.kind.name != "Potato": + self.menubar.entryconfig(4, state="disabled") + + # themes menu + self.menu_themes = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(menu=self.menu_themes, label="Themes") + tcl_themes = [ + "light", + "dark", + ] + [ + self.menu_themes.add_command( + label=theme.capitalize(), + command=partial(self.load_theme, theme), + ) + for theme in tcl_themes + ] + self.menu_themes.entryconfig( + 0 if self.configuration_app["theme"]["mode"] == "light" else 1, + state="disabled", + ) + if not _base_vals.themes_enabled: + self.menubar.entryconfig(6, state="disabled") + + # Help menu + self.menu_help = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(menu=self.menu_help, label="Help") + self.menu_help.add_command( + label="Voicemeeter Site", + command=self.documentation, + ) + self.menu_help.add_command( + label="Source Code", + command=self.github, + ) + self.menu_help.add_command( + label="App Creator", + command=self.onyxandiris, + ) + + @property + def target(self): + """use the correct interface""" + return self._parent.target + + @property + def configuration_app(self): + return configuration["app"] + + @configuration_app.setter + def configuration_app(self, val): + self.configuration_app = val + + @property + def configuration_vban(self): + return configuration["vban"] + + def action_invoke_voicemeeter(self, cmd): + getattr(self.target.command, cmd)() + + def action_set_voicemeeter(self, cmd, val=True): + setattr(self.target.command, cmd, val) + + def load_profile(self, profile): + self.target.apply_profile(profile) + + def load_defaults(self): + resp = messagebox.askyesno( + message="Are you sure you want to Reset values to defaults?\nPhysical strips B1, Virtual strips A1\nMono, Solo, Mute, EQ all OFF" + ) + if resp: + self.target.apply_profile("base") + + def always_on_top(self): + self._parent.attributes("-topmost", self._is_topmost.get()) + self._parent.update() + + def switch_orientation(self, extends_horizontal: bool = True, *args): + _base_vals.extends_horizontal = extends_horizontal + if extends_horizontal: + self.menu_extends.entryconfig(0, state="disabled") + self.menu_extends.entryconfig(1, state="normal") + else: + self.menu_extends.entryconfig(1, state="disabled") + self.menu_extends.entryconfig(0, state="normal") + + def set_submix(self, i): + if _base_vals.submixes != i: + _base_vals.submixes = i + if self._parent.submix_frame is not None: + self._parent.submix_frame.destroy() + self._parent.nav_frame.show_submix() + for j, var in enumerate(self._selected_bus): + var.set(True if i == j else False) + + def load_theme(self, theme): + self.tk.call("set_theme", theme) + self.configuration_app["theme"]["mode"] = theme + self.menu_themes.entryconfig( + 0, + state=f"{'disabled' if theme == 'light' else 'normal'}", + ) + self.menu_themes.entryconfig( + 1, + state=f"{'disabled' if theme == 'dark' else 'normal'}", + ) + [ + menu.config(bg=f"{'black' if theme == 'dark' else 'white'}") + for menu in self.menubar.winfo_children() + if isinstance(menu, tk.Menu) + ] + self.menu_lock.config(bg=f"{'black' if theme == 'dark' else 'white'}") + self.menu_profiles_load.config(bg=f"{'black' if theme == 'dark' else 'white'}") + [ + menu.config(bg=f"{'black' if theme == 'dark' else 'white'}") + for menu in self.menu_vban.winfo_children() + if isinstance(menu, tk.Menu) + ] + + def vban_connect(self, i): + [ + self.menu_vban.entryconfig(j, state="disabled") + for j, _ in enumerate(self.menu_vban.winfo_children()) + if j != i + ] + + opts = {} + opts |= self.configuration_vban[f"connection-{i+1}"] + kind_id = opts.pop("kind") + self.vban_conns[i] = vbancmd.connect(kind_id, **opts) + # login to vban interface + self.vban_conns[i].login() + # destroy the current App frames + self._parent._destroy_top_level_frames() + _base_vals.vban_connected = True + # build new app frames according to a kind + kind = kind_get(kind_id) + self._parent._make_app(kind, self.vban_conns[i]) + target_menu = getattr(self, f"menu_vban_{i+1}") + target_menu.entryconfig(0, state="disabled") + target_menu.entryconfig(1, state="normal") + self.menubar.entryconfig( + 4, state=f"{'normal' if kind.name == 'Potato' else 'disabled'}" + ) + + def vban_disconnect(self, i): + # destroy the current App frames + self._parent._destroy_top_level_frames() + _base_vals.vban_connected = False + # logout of vban interface + i_to_close = self.vban_conns[i] + self.vban_conns[i] = None + i_to_close.logout() + # build new app frames according to a kind + kind = kind_get(self._vmr.type) + # destroy the current App frames + self._parent._destroy_top_level_frames() + self._parent._make_app(kind, None) + target_menu = getattr(self, f"menu_vban_{i+1}") + target_menu.entryconfig(0, state="normal") + target_menu.entryconfig(1, state="disabled") + + self.after(15000, self.enable_vban_menus) + + def enable_vban_menus(self): + [ + self.menu_vban.entryconfig(j, state="normal") + for j, _ in enumerate(self.menu_vban.winfo_children()) + ] + + def documentation(self): + webbrowser.open_new(r"https://voicemeeter.com/") + + def github(self): + webbrowser.open_new(r"https://github.com/onyx-and-iris") + + def onyxandiris(self): + webbrowser.open_new(r"https://onyxandiris.online") diff --git a/vmcompact/navigation.py b/vmcompact/navigation.py new file mode 100644 index 0000000..7d933c9 --- /dev/null +++ b/vmcompact/navigation.py @@ -0,0 +1,156 @@ +import tkinter as tk +from tkinter import ttk + +from .channels import ChannelFrame +from .gainlayer import SubMixFrame +from .data import _base_vals + + +class Navigation(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self._parent = parent + self.s = parent.styletable + + self.submix = tk.BooleanVar() + self.channel = tk.BooleanVar() + self.extend = tk.BooleanVar() + self.info = tk.BooleanVar() + + self.channel_text = tk.StringVar() + self.channel_text.set(parent.channel_frame.identifier.upper()) + self.extend_text = tk.StringVar() + self.extend_text.set("EXTEND") + self.info_text = tk.StringVar() + self._parent.submix_frame = None + + self._make_widgets() + + self.col_row_configure() + + def _make_widgets(self): + """Creates the navigation buttons""" + self.submix_button = ttk.Checkbutton( + self, + text="SUBMIX", + command=self.show_submix, + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'Submix.TButton'}", + variable=self.submix, + ) + self.channel_button = ttk.Checkbutton( + self, + textvariable=self.channel_text, + command=self.switch_channel, + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'Channel.TButton'}", + variable=self.channel, + ) + self.extend_button = ttk.Checkbutton( + self, + textvariable=self.extend_text, + command=self.extend_frame, + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'Extend.TButton'}", + variable=self.extend, + ) + self.info_button = ttk.Checkbutton( + self, + textvariable=self.info_text, + style=f"{'Toggle.TButton' if _base_vals.using_theme else f'Rec.TButton'}", + variable=self.info, + ) + self.info_button["state"] = "active" + + """ Position navigation buttons """ + self.submix_button.grid(column=0, row=0) + self.channel_button.grid(column=0, row=1, rowspan=1) + self.extend_button.grid(column=0, row=2) + self.info_button.grid(column=0, row=3) + + if self._parent.kind.name != "Potato": + self.submix_button["state"] = "disabled" + + def show_submix(self): + if self.submix.get(): + if _base_vals.extends_horizontal: + self._parent.submix_frame = SubMixFrame(self._parent) + self._parent.submix_frame.grid(row=0, column=2) + if self._parent.bus_frame: + self._parent.bus_frame.grid_remove() + else: + self._parent.submix_frame = SubMixFrame(self._parent) + self._parent.submix_frame.grid(row=2, column=0, sticky=(tk.W)) + if self._parent.bus_frame: + self._parent.bus_frame.grid_remove() + else: + if _base_vals.extends_horizontal: + self._parent.submix_frame.destroy() + if self._parent.bus_frame: + self._parent.bus_frame.grid() + else: + self._parent.columnconfigure(1, weight=0) + else: + self._parent.submix_frame.destroy() + if self._parent.bus_frame: + self._parent.bus_frame.grid() + else: + self._parent.rowconfigure(2, weight=0, minsize=0) + + if not _base_vals.using_theme: + self.s.configure( + f"Submix.TButton", + background=f'{"purple" if self.submix.get() else "white"}', + ) + + def switch_channel(self): + if self.channel_text.get() == "STRIP": + self._parent.bus_frame = ChannelFrame.make_buses(self._parent) + self._parent.bus_frame.grid(row=0, column=0) + self._parent.channel_frame.destroy() + else: + self._parent.channel_frame = ChannelFrame.make_strips(self._parent) + self._parent.channel_frame.grid(row=0, column=0) + self._parent.bus_frame.destroy() + + self.extend_button["state"] = ( + "disabled" if self.channel_text.get() == "STRIP" else "normal" + ) + [frame.destroy() for frame in self._parent.configframes] + self.channel_text.set("BUS" if self.channel_text.get() == "STRIP" else "STRIP") + + def extend_frame(self): + if self.extend.get(): + self.channel_button["state"] = "disabled" + self._parent.bus_frame = ChannelFrame.make_buses(self._parent) + if _base_vals.extends_horizontal: + self._parent.bus_frame.grid(row=0, column=2) + else: + self._parent.bus_frame.grid(row=2, column=0, sticky=(tk.W)) + else: + [ + frame.destroy() + for frame in self._parent.configframes + if "!busconfig" in str(frame) + ] + self._parent.bus_frame.destroy() + self._parent.bus_frame = None + self.channel_button["state"] = "normal" + + if self._parent.submix_frame: + self._parent.submix_frame.destroy() + self.submix.set(False) + if not _base_vals.using_theme: + self.s.configure( + f"Submix.TButton", + background=f'{"purple" if self.submix.get() else "white"}', + ) + + self.extend_text.set("REDUCE" if self.extend.get() else "EXTEND") + + def col_row_configure(self): + [ + child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S, tk.W, tk.E)) + for child in self.winfo_children() + if isinstance(child, ttk.Checkbutton) + ] + + self.rowconfigure(1, minsize=self._parent.channel_frame.height - 18) + self.grid(sticky=(tk.N)) diff --git a/vmcompact/sun-valley-theme/LICENSE b/vmcompact/sun-valley-theme/LICENSE new file mode 100644 index 0000000..0212030 --- /dev/null +++ b/vmcompact/sun-valley-theme/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 rdbende + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vmcompact/sun-valley-theme/sun-valley.tcl b/vmcompact/sun-valley-theme/sun-valley.tcl new file mode 100644 index 0000000..a1b55d2 --- /dev/null +++ b/vmcompact/sun-valley-theme/sun-valley.tcl @@ -0,0 +1,88 @@ +# Copyright © 2021 rdbende + +source [file join [file dirname [info script]] theme light.tcl] +source [file join [file dirname [info script]] theme dark.tcl] + +option add *tearOff 0 + +proc set_theme {mode} { + if {$mode == "dark"} { + ttk::style theme use "sun-valley-dark" + + array set colors { + -fg "#ffffff" + -bg "#1c1c1c" + -disabledfg "#595959" + -selectfg "#ffffff" + -selectbg "#2f60d8" + } + + ttk::style configure . \ + -background $colors(-bg) \ + -foreground $colors(-fg) \ + -troughcolor $colors(-bg) \ + -focuscolor $colors(-selectbg) \ + -selectbackground $colors(-selectbg) \ + -selectforeground $colors(-selectfg) \ + -insertwidth 1 \ + -insertcolor $colors(-fg) \ + -fieldbackground $colors(-selectbg) \ + -font {"Segoe UI" 10} \ + -borderwidth 0 \ + -relief flat + + tk_setPalette \ + background [ttk::style lookup . -background] \ + foreground [ttk::style lookup . -foreground] \ + highlightColor [ttk::style lookup . -focuscolor] \ + selectBackground [ttk::style lookup . -selectbackground] \ + selectForeground [ttk::style lookup . -selectforeground] \ + activeBackground [ttk::style lookup . -selectbackground] \ + activeForeground [ttk::style lookup . -selectforeground] + + ttk::style map . -foreground [list disabled $colors(-disabledfg)] + + option add *font [ttk::style lookup . -font] + option add *Menu.selectcolor $colors(-fg) + option add *Menu.background #2f2f2f + + } elseif {$mode == "light"} { + ttk::style theme use "sun-valley-light" + + array set colors { + -fg "#202020" + -bg "#fafafa" + -disabledfg "#a0a0a0" + -selectfg "#ffffff" + -selectbg "#2f60d8" + } + + ttk::style configure . \ + -background $colors(-bg) \ + -foreground $colors(-fg) \ + -troughcolor $colors(-bg) \ + -focuscolor $colors(-selectbg) \ + -selectbackground $colors(-selectbg) \ + -selectforeground $colors(-selectfg) \ + -insertwidth 1 \ + -insertcolor $colors(-fg) \ + -fieldbackground $colors(-selectbg) \ + -font {"Segoe UI" 10} \ + -borderwidth 0 \ + -relief flat + + tk_setPalette background [ttk::style lookup . -background] \ + foreground [ttk::style lookup . -foreground] \ + highlightColor [ttk::style lookup . -focuscolor] \ + selectBackground [ttk::style lookup . -selectbackground] \ + selectForeground [ttk::style lookup . -selectforeground] \ + activeBackground [ttk::style lookup . -selectbackground] \ + activeForeground [ttk::style lookup . -selectforeground] + + ttk::style map . -foreground [list disabled $colors(-disabledfg)] + + option add *font [ttk::style lookup . -font] + option add *Menu.selectcolor $colors(-fg) + option add *Menu.background #e7e7e7 + } +} diff --git a/vmcompact/sun-valley-theme/theme/dark.tcl b/vmcompact/sun-valley-theme/theme/dark.tcl new file mode 100644 index 0000000..2363eca --- /dev/null +++ b/vmcompact/sun-valley-theme/theme/dark.tcl @@ -0,0 +1,521 @@ +# Copyright © 2021 rdbende + +# A stunning dark theme for ttk based on Microsoft's Sun Valley visual style + +package require Tk 8.6 + +namespace eval ttk::theme::sun-valley-dark { + variable version 1.0 + package provide ttk::theme::sun-valley-dark $version + + ttk::style theme create sun-valley-dark -parent clam -settings { + proc load_images {imgdir} { + variable images + foreach file [glob -directory $imgdir *.png] { + set images([file tail [file rootname $file]]) \ + [image create photo -file $file -format png] + } + } + + load_images [file join [file dirname [info script]] dark] + + array set colors { + -fg "#ffffff" + -bg "#1c1c1c" + -disabledfg "#595959" + -selectfg "#ffffff" + -selectbg "#2f60d8" + } + + ttk::style layout TButton { + Button.button -children { + Button.padding -children { + Button.label -side left -expand 1 + } + } + } + + ttk::style layout Toolbutton { + Toolbutton.button -children { + Toolbutton.padding -children { + Toolbutton.label -side left -expand 1 + } + } + } + + ttk::style layout TMenubutton { + Menubutton.button -children { + Menubutton.padding -children { + Menubutton.label -side left -expand 1 + Menubutton.indicator -side right -sticky nsew + } + } + } + + ttk::style layout TOptionMenu { + OptionMenu.button -children { + OptionMenu.padding -children { + OptionMenu.label -side left -expand 1 + OptionMenu.indicator -side right -sticky nsew + } + } + } + + ttk::style layout Accent.TButton { + AccentButton.button -children { + AccentButton.padding -children { + AccentButton.label -side left -expand 1 + } + } + } + + ttk::style layout Titlebar.TButton { + TitlebarButton.button -children { + TitlebarButton.padding -children { + TitlebarButton.label -side left -expand 1 + } + } + } + + ttk::style layout Close.Titlebar.TButton { + CloseButton.button -children { + CloseButton.padding -children { + CloseButton.label -side left -expand 1 + } + } + } + + ttk::style layout TCheckbutton { + Checkbutton.button -children { + Checkbutton.padding -children { + Checkbutton.indicator -side left + Checkbutton.label -side right -expand 1 + } + } + } + + ttk::style layout Switch.TCheckbutton { + Switch.button -children { + Switch.padding -children { + Switch.indicator -side left + Switch.label -side right -expand 1 + } + } + } + + ttk::style layout Toggle.TButton { + ToggleButton.button -children { + ToggleButton.padding -children { + ToggleButton.label -side left -expand 1 + } + } + } + + ttk::style layout TRadiobutton { + Radiobutton.button -children { + Radiobutton.padding -children { + Radiobutton.indicator -side left + Radiobutton.label -side right -expand 1 + } + } + } + + ttk::style layout Vertical.TScrollbar { + Vertical.Scrollbar.trough -sticky ns -children { + Vertical.Scrollbar.uparrow -side top + Vertical.Scrollbar.downarrow -side bottom + Vertical.Scrollbar.thumb -expand 1 + } + } + + ttk::style layout Horizontal.TScrollbar { + Horizontal.Scrollbar.trough -sticky ew -children { + Horizontal.Scrollbar.leftarrow -side left + Horizontal.Scrollbar.rightarrow -side right + Horizontal.Scrollbar.thumb -expand 1 + } + } + + ttk::style layout TSeparator { + TSeparator.separator -sticky nsew + } + + ttk::style layout TCombobox { + Combobox.field -sticky nsew -children { + Combobox.padding -expand 1 -sticky nsew -children { + Combobox.textarea -sticky nsew + } + } + null -side right -sticky ns -children { + Combobox.arrow -sticky nsew + } + } + + ttk::style layout TSpinbox { + Spinbox.field -sticky nsew -children { + Spinbox.padding -expand 1 -sticky nsew -children { + Spinbox.textarea -sticky nsew + } + + } + null -side right -sticky nsew -children { + Spinbox.uparrow -side left -sticky nsew + Spinbox.downarrow -side right -sticky nsew + } + } + + ttk::style layout Card.TFrame { + Card.field { + Card.padding -expand 1 + } + } + + ttk::style layout TLabelframe { + Labelframe.border { + Labelframe.padding -expand 1 -children { + Labelframe.label -side left + } + } + } + + ttk::style layout TNotebook { + Notebook.border -children { + TNotebook.Tab -expand 1 + Notebook.client -sticky nsew + } + } + + ttk::style layout Treeview.Item { + Treeitem.padding -sticky nsew -children { + Treeitem.image -side left -sticky {} + Treeitem.indicator -side left -sticky {} + Treeitem.text -side left -sticky {} + } + } + + # Button + ttk::style configure TButton -padding {8 4} -anchor center -foreground $colors(-fg) + + ttk::style map TButton -foreground \ + [list disabled #7a7a7a \ + pressed #d0d0d0] + + ttk::style element create Button.button image \ + [list $images(button-rest) \ + {selected disabled} $images(button-disabled) \ + disabled $images(button-disabled) \ + selected $images(button-rest) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + # Toolbutton + ttk::style configure Toolbutton -padding {8 4} -anchor center + + ttk::style element create Toolbutton.button image \ + [list $images(empty) \ + {selected disabled} $images(button-disabled) \ + selected $images(button-rest) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + # Menubutton + ttk::style configure TMenubutton -padding {8 4 0 4} + + ttk::style element create Menubutton.button \ + image [list $images(button-rest) \ + disabled $images(button-disabled) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + ttk::style element create Menubutton.indicator image $images(arrow-down) -width 28 -sticky {} + + # OptionMenu + ttk::style configure TOptionMenu -padding {8 4 0 4} + + ttk::style element create OptionMenu.button \ + image [list $images(button-rest) \ + disabled $images(button-disabled) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + ttk::style element create OptionMenu.indicator image $images(arrow-down) -width 28 -sticky {} + + # Accent.TButton + ttk::style configure Accent.TButton -padding {8 4} -anchor center -foreground #000000 + + ttk::style map Accent.TButton -foreground \ + [list pressed #25536a \ + disabled #a5a5a5] + + ttk::style element create AccentButton.button image \ + [list $images(button-accent-rest) \ + {selected disabled} $images(button-accent-disabled) \ + disabled $images(button-accent-disabled) \ + selected $images(button-accent-rest) \ + pressed $images(button-accent-pressed) \ + active $images(button-accent-hover) \ + ] -border 4 -sticky nsew + + # Titlebar.TButton + ttk::style configure Titlebar.TButton -padding {8 4} -anchor center -foreground #ffffff + + ttk::style map Titlebar.TButton -foreground \ + [list disabled #6f6f6f \ + pressed #d1d1d1 \ + active #ffffff] + + ttk::style element create TitlebarButton.button image \ + [list $images(empty) \ + disabled $images(empty) \ + pressed $images(button-titlebar-pressed) \ + active $images(button-titlebar-hover) \ + ] -border 4 -sticky nsew + + # Close.Titlebar.TButton + ttk::style configure Close.Titlebar.TButton -padding {8 4} -anchor center -foreground #ffffff + + ttk::style map Close.Titlebar.TButton -foreground \ + [list disabled #6f6f6f \ + pressed #e8bfbb \ + active #ffffff] + + ttk::style element create CloseButton.button image \ + [list $images(empty) \ + disabled $images(empty) \ + pressed $images(button-close-pressed) \ + active $images(button-close-hover) \ + ] -border 4 -sticky nsew + + # Checkbutton + ttk::style configure TCheckbutton -padding 4 + + ttk::style element create Checkbutton.indicator image \ + [list $images(check-unsel-rest) \ + {alternate disabled} $images(check-tri-disabled) \ + {selected disabled} $images(check-disabled) \ + disabled $images(check-unsel-disabled) \ + {pressed alternate} $images(check-tri-hover) \ + {active alternate} $images(check-tri-hover) \ + alternate $images(check-tri-rest) \ + {pressed selected} $images(check-hover) \ + {active selected} $images(check-hover) \ + selected $images(check-rest) \ + {pressed !selected} $images(check-unsel-pressed) \ + active $images(check-unsel-hover) \ + ] -width 26 -sticky w + + # Switch.TCheckbutton + ttk::style element create Switch.indicator image \ + [list $images(switch-off-rest) \ + {selected disabled} $images(switch-on-disabled) \ + disabled $images(switch-off-disabled) \ + {pressed selected} $images(switch-on-pressed) \ + {active selected} $images(switch-on-hover) \ + selected $images(switch-on-rest) \ + {pressed !selected} $images(switch-off-pressed) \ + active $images(switch-off-hover) \ + ] -width 46 -sticky w + + # Toggle.TButton + ttk::style configure Toggle.TButton -padding {8 4 8 4} -anchor center -foreground $colors(-fg) + + ttk::style map Toggle.TButton -foreground \ + [list {selected disabled} #a5a5a5 \ + {selected pressed} #d0d0d0 \ + selected #000000 \ + pressed #25536a \ + disabled #7a7a7a + ] + + ttk::style element create ToggleButton.button image \ + [list $images(button-rest) \ + {selected disabled} $images(button-accent-disabled) \ + disabled $images(button-disabled) \ + {pressed selected} $images(button-rest) \ + {active selected} $images(button-accent-hover) \ + selected $images(button-accent-rest) \ + {pressed !selected} $images(button-accent-rest) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + # Radiobutton + ttk::style configure TRadiobutton -padding 4 + + ttk::style element create Radiobutton.indicator image \ + [list $images(radio-unsel-rest) \ + {selected disabled} $images(radio-disabled) \ + disabled $images(radio-unsel-disabled) \ + {pressed selected} $images(radio-pressed) \ + {active selected} $images(radio-hover) \ + selected $images(radio-rest) \ + {pressed !selected} $images(radio-unsel-pressed) \ + active $images(radio-unsel-hover) \ + ] -width 26 -sticky w + + # Scrollbar + ttk::style element create Horizontal.Scrollbar.trough image $images(scroll-hor-trough) -sticky ew -border 6 + ttk::style element create Horizontal.Scrollbar.thumb image $images(scroll-hor-thumb) -sticky ew -border 3 + + ttk::style element create Horizontal.Scrollbar.rightarrow image $images(scroll-right) -sticky {} -width 12 + ttk::style element create Horizontal.Scrollbar.leftarrow image $images(scroll-left) -sticky {} -width 12 + + ttk::style element create Vertical.Scrollbar.trough image $images(scroll-vert-trough) -sticky ns -border 6 + ttk::style element create Vertical.Scrollbar.thumb image $images(scroll-vert-thumb) -sticky ns -border 3 + + ttk::style element create Vertical.Scrollbar.uparrow image $images(scroll-up) -sticky {} -height 12 + ttk::style element create Vertical.Scrollbar.downarrow image $images(scroll-down) -sticky {} -height 12 + + # Scale + ttk::style element create Horizontal.Scale.trough image $images(scale-trough-hor) \ + -border 5 -padding 0 + + ttk::style element create Vertical.Scale.trough image $images(scale-trough-vert) \ + -border 5 -padding 0 + + ttk::style element create Scale.slider \ + image [list $images(scale-thumb-rest) \ + disabled $images(scale-thumb-disabled) \ + pressed $images(scale-thumb-pressed) \ + active $images(scale-thumb-hover) \ + ] -sticky {} + + # Progressbar + ttk::style element create Horizontal.Progressbar.trough image $images(progress-trough-hor) \ + -border 1 -sticky ew + + ttk::style element create Horizontal.Progressbar.pbar image $images(progress-pbar-hor) \ + -border 2 -sticky ew + + ttk::style element create Vertical.Progressbar.trough image $images(progress-trough-vert) \ + -border 1 -sticky ns + + ttk::style element create Vertical.Progressbar.pbar image $images(progress-pbar-vert) \ + -border 2 -sticky ns + + # Entry + ttk::style configure TEntry -foreground $colors(-fg) + + ttk::style map TEntry -foreground \ + [list disabled #757575 \ + pressed #cfcfcf + ] + + ttk::style element create Entry.field \ + image [list $images(entry-rest) \ + {focus hover !invalid} $images(entry-focus) \ + invalid $images(entry-invalid) \ + disabled $images(entry-disabled) \ + {focus !invalid} $images(entry-focus) \ + hover $images(entry-hover) \ + ] -border 5 -padding 8 -sticky nsew + + # Combobox + ttk::style configure TCombobox -foreground $colors(-fg) + + ttk::style map TCombobox -foreground \ + [list disabled #757575 \ + pressed #cfcfcf + ] + + ttk::style configure ComboboxPopdownFrame -borderwidth 1 -relief solid + + ttk::style map TCombobox -selectbackground [list \ + {readonly hover} $colors(-selectbg) \ + {readonly focus} $colors(-selectbg) \ + ] -selectforeground [list \ + {readonly hover} $colors(-selectfg) \ + {readonly focus} $colors(-selectfg) \ + ] + + ttk::style element create Combobox.field \ + image [list $images(entry-rest) \ + {readonly disabled} $images(button-disabled) \ + {readonly pressed} $images(button-pressed) \ + {readonly hover} $images(button-hover) \ + readonly $images(button-rest) \ + invalid $images(entry-invalid) \ + disabled $images(entry-disabled) \ + focus $images(entry-focus) \ + hover $images(entry-hover) \ + ] -border 5 -padding {8 8 28 8} + + ttk::style element create Combobox.arrow image $images(arrow-down) -width 35 -sticky {} + + # Spinbox + ttk::style configure TSpinbox -foreground $colors(-fg) + + ttk::style map TSpinbox -foreground \ + [list disabled #757575 \ + pressed #cfcfcf + ] + + ttk::style element create Spinbox.field \ + image [list $images(entry-rest) \ + invalid $images(entry-invalid) \ + disabled $images(entry-disabled) \ + focus $images(entry-focus) \ + hover $images(entry-hover) \ + ] -border 5 -padding {8 8 54 8} -sticky nsew + + ttk::style element create Spinbox.uparrow image $images(arrow-up) -width 35 -sticky {} + ttk::style element create Spinbox.downarrow image $images(arrow-down) -width 35 -sticky {} + + # Sizegrip + ttk::style element create Sizegrip.sizegrip image $images(sizegrip) \ + -sticky nsew + + # Separator + ttk::style element create TSeparator.separator image $images(separator) + + # Card + ttk::style element create Card.field image $images(card) \ + -border 10 -padding 4 -sticky nsew + + # Labelframe + ttk::style element create Labelframe.border image $images(card) \ + -border 5 -padding 4 -sticky nsew + + # Notebook + ttk::style configure TNotebook -padding 1 + + ttk::style element create Notebook.border \ + image $images(notebook-border) -border 5 -padding 5 + + ttk::style element create Notebook.client image $images(notebook) + + ttk::style element create Notebook.tab \ + image [list $images(tab-rest) \ + selected $images(tab-selected) \ + active $images(tab-hover) \ + ] -border 13 -padding {16 14 16 6} -height 32 + + # Treeview + ttk::style element create Treeview.field image $images(card) \ + -border 5 + + ttk::style element create Treeheading.cell \ + image [list $images(treeheading-rest) \ + pressed $images(treeheading-pressed) \ + active $images(treeheading-hover) + ] -border 5 -padding 15 -sticky nsew + + ttk::style element create Treeitem.indicator \ + image [list $images(arrow-right) \ + user2 $images(empty) \ + user1 $images(arrow-down) \ + ] -width 26 -sticky {} + + ttk::style configure Treeview -background $colors(-bg) -rowheight [expr {[font metrics font -linespace] + 2}] + ttk::style map Treeview \ + -background [list selected #292929] \ + -foreground [list selected $colors(-selectfg)] + + # Panedwindow + # Insane hack to remove clam's ugly sash + ttk::style configure Sash -gripcount 0 + } +} \ No newline at end of file diff --git a/vmcompact/sun-valley-theme/theme/dark/arrow-down.png b/vmcompact/sun-valley-theme/theme/dark/arrow-down.png new file mode 100644 index 0000000..122ee45 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/arrow-down.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/arrow-right.png b/vmcompact/sun-valley-theme/theme/dark/arrow-right.png new file mode 100644 index 0000000..2638d88 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/arrow-right.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/arrow-up.png b/vmcompact/sun-valley-theme/theme/dark/arrow-up.png new file mode 100644 index 0000000..f935a0d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/arrow-up.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-accent-disabled.png b/vmcompact/sun-valley-theme/theme/dark/button-accent-disabled.png new file mode 100644 index 0000000..bf7bd9b Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-accent-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-accent-hover.png b/vmcompact/sun-valley-theme/theme/dark/button-accent-hover.png new file mode 100644 index 0000000..8aea9dd Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-accent-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-accent-pressed.png b/vmcompact/sun-valley-theme/theme/dark/button-accent-pressed.png new file mode 100644 index 0000000..edc1114 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-accent-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-accent-rest.png b/vmcompact/sun-valley-theme/theme/dark/button-accent-rest.png new file mode 100644 index 0000000..75e64f8 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-accent-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-close-hover.png b/vmcompact/sun-valley-theme/theme/dark/button-close-hover.png new file mode 100644 index 0000000..6fc0c00 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-close-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-close-pressed.png b/vmcompact/sun-valley-theme/theme/dark/button-close-pressed.png new file mode 100644 index 0000000..6023dc1 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-close-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-disabled.png b/vmcompact/sun-valley-theme/theme/dark/button-disabled.png new file mode 100644 index 0000000..27eb005 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-hover.png b/vmcompact/sun-valley-theme/theme/dark/button-hover.png new file mode 100644 index 0000000..84f2652 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-pressed.png b/vmcompact/sun-valley-theme/theme/dark/button-pressed.png new file mode 100644 index 0000000..a1c5257 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-rest.png b/vmcompact/sun-valley-theme/theme/dark/button-rest.png new file mode 100644 index 0000000..ec427ed Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-titlebar-hover.png b/vmcompact/sun-valley-theme/theme/dark/button-titlebar-hover.png new file mode 100644 index 0000000..fcb3751 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-titlebar-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/button-titlebar-pressed.png b/vmcompact/sun-valley-theme/theme/dark/button-titlebar-pressed.png new file mode 100644 index 0000000..2ed0623 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/button-titlebar-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/card.png b/vmcompact/sun-valley-theme/theme/dark/card.png new file mode 100644 index 0000000..d87fefc Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/card.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-disabled.png b/vmcompact/sun-valley-theme/theme/dark/check-disabled.png new file mode 100644 index 0000000..f766eba Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-hover.png b/vmcompact/sun-valley-theme/theme/dark/check-hover.png new file mode 100644 index 0000000..a780488 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-pressed.png b/vmcompact/sun-valley-theme/theme/dark/check-pressed.png new file mode 100644 index 0000000..4f9d1fc Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-rest.png b/vmcompact/sun-valley-theme/theme/dark/check-rest.png new file mode 100644 index 0000000..73d4c36 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-tri-disabled.png b/vmcompact/sun-valley-theme/theme/dark/check-tri-disabled.png new file mode 100644 index 0000000..a9d31c7 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-tri-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-tri-hover.png b/vmcompact/sun-valley-theme/theme/dark/check-tri-hover.png new file mode 100644 index 0000000..ed218a0 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-tri-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-tri-pressed.png b/vmcompact/sun-valley-theme/theme/dark/check-tri-pressed.png new file mode 100644 index 0000000..68d7a99 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-tri-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-tri-rest.png b/vmcompact/sun-valley-theme/theme/dark/check-tri-rest.png new file mode 100644 index 0000000..26edcdb Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-tri-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-unsel-disabled.png b/vmcompact/sun-valley-theme/theme/dark/check-unsel-disabled.png new file mode 100644 index 0000000..69f850f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-unsel-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-unsel-hover.png b/vmcompact/sun-valley-theme/theme/dark/check-unsel-hover.png new file mode 100644 index 0000000..6d00402 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-unsel-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-unsel-pressed.png b/vmcompact/sun-valley-theme/theme/dark/check-unsel-pressed.png new file mode 100644 index 0000000..67d6bb2 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-unsel-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/check-unsel-rest.png b/vmcompact/sun-valley-theme/theme/dark/check-unsel-rest.png new file mode 100644 index 0000000..10cd31b Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/check-unsel-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/empty.png b/vmcompact/sun-valley-theme/theme/dark/empty.png new file mode 100644 index 0000000..2218363 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/empty.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/entry-disabled.png b/vmcompact/sun-valley-theme/theme/dark/entry-disabled.png new file mode 100644 index 0000000..9d25dc8 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/entry-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/entry-focus.png b/vmcompact/sun-valley-theme/theme/dark/entry-focus.png new file mode 100644 index 0000000..30310fb Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/entry-focus.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/entry-hover.png b/vmcompact/sun-valley-theme/theme/dark/entry-hover.png new file mode 100644 index 0000000..6b93830 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/entry-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/entry-invalid.png b/vmcompact/sun-valley-theme/theme/dark/entry-invalid.png new file mode 100644 index 0000000..7304b24 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/entry-invalid.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/entry-rest.png b/vmcompact/sun-valley-theme/theme/dark/entry-rest.png new file mode 100644 index 0000000..e876752 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/entry-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/notebook-border.png b/vmcompact/sun-valley-theme/theme/dark/notebook-border.png new file mode 100644 index 0000000..0827a07 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/notebook-border.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/notebook.png b/vmcompact/sun-valley-theme/theme/dark/notebook.png new file mode 100644 index 0000000..051e529 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/notebook.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/progress-pbar-hor.png b/vmcompact/sun-valley-theme/theme/dark/progress-pbar-hor.png new file mode 100644 index 0000000..8206cf1 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/progress-pbar-hor.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/progress-pbar-vert.png b/vmcompact/sun-valley-theme/theme/dark/progress-pbar-vert.png new file mode 100644 index 0000000..3d0cb29 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/progress-pbar-vert.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/progress-trough-hor.png b/vmcompact/sun-valley-theme/theme/dark/progress-trough-hor.png new file mode 100644 index 0000000..3aec8a7 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/progress-trough-hor.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/progress-trough-vert.png b/vmcompact/sun-valley-theme/theme/dark/progress-trough-vert.png new file mode 100644 index 0000000..22a8c1c Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/progress-trough-vert.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-disabled.png b/vmcompact/sun-valley-theme/theme/dark/radio-disabled.png new file mode 100644 index 0000000..965136d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-hover.png b/vmcompact/sun-valley-theme/theme/dark/radio-hover.png new file mode 100644 index 0000000..b3c19b7 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-pressed.png b/vmcompact/sun-valley-theme/theme/dark/radio-pressed.png new file mode 100644 index 0000000..87bf871 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-rest.png b/vmcompact/sun-valley-theme/theme/dark/radio-rest.png new file mode 100644 index 0000000..a86b523 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-unsel-disabled.png b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-disabled.png new file mode 100644 index 0000000..ec7dd91 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-unsel-hover.png b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-hover.png new file mode 100644 index 0000000..9724bd1 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-unsel-pressed.png b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-pressed.png new file mode 100644 index 0000000..1534a96 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/radio-unsel-rest.png b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-rest.png new file mode 100644 index 0000000..ad38ece Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/radio-unsel-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scale-thumb-disabled.png b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-disabled.png new file mode 100644 index 0000000..ba77f1d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scale-thumb-hover.png b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-hover.png new file mode 100644 index 0000000..8398922 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scale-thumb-pressed.png b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-pressed.png new file mode 100644 index 0000000..70029b3 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scale-thumb-rest.png b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-rest.png new file mode 100644 index 0000000..f6571b9 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scale-thumb-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scale-trough-hor.png b/vmcompact/sun-valley-theme/theme/dark/scale-trough-hor.png new file mode 100644 index 0000000..7fa2bf4 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scale-trough-hor.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scale-trough-vert.png b/vmcompact/sun-valley-theme/theme/dark/scale-trough-vert.png new file mode 100644 index 0000000..205fed8 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scale-trough-vert.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-down.png b/vmcompact/sun-valley-theme/theme/dark/scroll-down.png new file mode 100644 index 0000000..4c0e24f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-down.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-hor-thumb.png b/vmcompact/sun-valley-theme/theme/dark/scroll-hor-thumb.png new file mode 100644 index 0000000..795a88a Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-hor-thumb.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-hor-trough.png b/vmcompact/sun-valley-theme/theme/dark/scroll-hor-trough.png new file mode 100644 index 0000000..89d0403 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-hor-trough.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-left.png b/vmcompact/sun-valley-theme/theme/dark/scroll-left.png new file mode 100644 index 0000000..f43538b Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-left.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-right.png b/vmcompact/sun-valley-theme/theme/dark/scroll-right.png new file mode 100644 index 0000000..a56511f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-right.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-up.png b/vmcompact/sun-valley-theme/theme/dark/scroll-up.png new file mode 100644 index 0000000..7ddba7f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-up.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-vert-thumb.png b/vmcompact/sun-valley-theme/theme/dark/scroll-vert-thumb.png new file mode 100644 index 0000000..572f33d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-vert-thumb.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/scroll-vert-trough.png b/vmcompact/sun-valley-theme/theme/dark/scroll-vert-trough.png new file mode 100644 index 0000000..c947ed1 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/scroll-vert-trough.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/separator.png b/vmcompact/sun-valley-theme/theme/dark/separator.png new file mode 100644 index 0000000..6e01f55 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/separator.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/sizegrip.png b/vmcompact/sun-valley-theme/theme/dark/sizegrip.png new file mode 100644 index 0000000..7080c04 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/sizegrip.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-off-disabled.png b/vmcompact/sun-valley-theme/theme/dark/switch-off-disabled.png new file mode 100644 index 0000000..4032c61 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-off-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-off-hover.png b/vmcompact/sun-valley-theme/theme/dark/switch-off-hover.png new file mode 100644 index 0000000..5a136bd Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-off-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-off-pressed.png b/vmcompact/sun-valley-theme/theme/dark/switch-off-pressed.png new file mode 100644 index 0000000..040e2ea Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-off-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-off-rest.png b/vmcompact/sun-valley-theme/theme/dark/switch-off-rest.png new file mode 100644 index 0000000..6c31bb2 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-off-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-on-disabled.png b/vmcompact/sun-valley-theme/theme/dark/switch-on-disabled.png new file mode 100644 index 0000000..c0d67c5 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-on-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-on-hover.png b/vmcompact/sun-valley-theme/theme/dark/switch-on-hover.png new file mode 100644 index 0000000..fd4de94 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-on-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-on-pressed.png b/vmcompact/sun-valley-theme/theme/dark/switch-on-pressed.png new file mode 100644 index 0000000..00e87c6 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-on-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/switch-on-rest.png b/vmcompact/sun-valley-theme/theme/dark/switch-on-rest.png new file mode 100644 index 0000000..52a19ea Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/switch-on-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/tab-hover.png b/vmcompact/sun-valley-theme/theme/dark/tab-hover.png new file mode 100644 index 0000000..43a113b Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/tab-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/tab-rest.png b/vmcompact/sun-valley-theme/theme/dark/tab-rest.png new file mode 100644 index 0000000..9753e06 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/tab-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/tab-selected.png b/vmcompact/sun-valley-theme/theme/dark/tab-selected.png new file mode 100644 index 0000000..3b39d0b Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/tab-selected.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/treeheading-hover.png b/vmcompact/sun-valley-theme/theme/dark/treeheading-hover.png new file mode 100644 index 0000000..beaaf13 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/treeheading-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/treeheading-pressed.png b/vmcompact/sun-valley-theme/theme/dark/treeheading-pressed.png new file mode 100644 index 0000000..9cd311d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/treeheading-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/dark/treeheading-rest.png b/vmcompact/sun-valley-theme/theme/dark/treeheading-rest.png new file mode 100644 index 0000000..374ed49 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/dark/treeheading-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light.tcl b/vmcompact/sun-valley-theme/theme/light.tcl new file mode 100644 index 0000000..ae548ea --- /dev/null +++ b/vmcompact/sun-valley-theme/theme/light.tcl @@ -0,0 +1,526 @@ +# Copyright © 2021 rdbende + +# A stunning light theme for ttk based on Microsoft's Sun Valley visual style + +package require Tk 8.6 + +namespace eval ttk::theme::sun-valley-light { + variable version 1.0 + package provide ttk::theme::sun-valley-light $version + + ttk::style theme create sun-valley-light -parent clam -settings { + proc load_images {imgdir} { + variable images + foreach file [glob -directory $imgdir *.png] { + set images([file tail [file rootname $file]]) \ + [image create photo -file $file -format png] + } + } + + load_images [file join [file dirname [info script]] light] + + array set colors { + -fg "#202020" + -bg "#fafafa" + -disabledfg "#a0a0a0" + -selectfg "#ffffff" + -selectbg "#2f60d8" + } + + ttk::style layout TButton { + Button.button -children { + Button.padding -children { + Button.label -side left -expand 1 + } + } + } + + ttk::style layout Toolbutton { + Toolbutton.button -children { + Toolbutton.padding -children { + Toolbutton.label -side left -expand 1 + } + } + } + + ttk::style layout TMenubutton { + Menubutton.button -children { + Menubutton.padding -children { + Menubutton.label -side left -expand 1 + Menubutton.indicator -side right -sticky nsew + } + } + } + + ttk::style layout TOptionMenu { + OptionMenu.button -children { + OptionMenu.padding -children { + OptionMenu.label -side left -expand 1 + OptionMenu.indicator -side right -sticky nsew + } + } + } + + ttk::style layout Accent.TButton { + AccentButton.button -children { + AccentButton.padding -children { + AccentButton.label -side left -expand 1 + } + } + } + + ttk::style layout Titlebar.TButton { + TitlebarButton.button -children { + TitlebarButton.padding -children { + TitlebarButton.label -side left -expand 1 + } + } + } + + ttk::style layout Close.Titlebar.TButton { + CloseButton.button -children { + CloseButton.padding -children { + CloseButton.label -side left -expand 1 + } + } + } + + ttk::style layout TCheckbutton { + Checkbutton.button -children { + Checkbutton.padding -children { + Checkbutton.indicator -side left + Checkbutton.label -side right -expand 1 + } + } + } + + ttk::style layout Switch.TCheckbutton { + Switch.button -children { + Switch.padding -children { + Switch.indicator -side left + Switch.label -side right -expand 1 + } + } + } + + ttk::style layout Toggle.TButton { + ToggleButton.button -children { + ToggleButton.padding -children { + ToggleButton.label -side left -expand 1 + } + } + } + + ttk::style layout TRadiobutton { + Radiobutton.button -children { + Radiobutton.padding -children { + Radiobutton.indicator -side left + Radiobutton.label -side right -expand 1 + } + } + } + + ttk::style layout Vertical.TScrollbar { + Vertical.Scrollbar.trough -sticky ns -children { + Vertical.Scrollbar.uparrow -side top + Vertical.Scrollbar.downarrow -side bottom + Vertical.Scrollbar.thumb -expand 1 + } + } + + ttk::style layout Horizontal.TScrollbar { + Horizontal.Scrollbar.trough -sticky ew -children { + Horizontal.Scrollbar.leftarrow -side left + Horizontal.Scrollbar.rightarrow -side right + Horizontal.Scrollbar.thumb -expand 1 + } + } + + ttk::style layout TSeparator { + TSeparator.separator -sticky nsew + } + + ttk::style layout TCombobox { + Combobox.field -sticky nsew -children { + Combobox.padding -expand 1 -sticky nsew -children { + Combobox.textarea -sticky nsew + } + } + null -side right -sticky ns -children { + Combobox.arrow -sticky nsew + } + } + + ttk::style layout TSpinbox { + Spinbox.field -sticky nsew -children { + Spinbox.padding -expand 1 -sticky nsew -children { + Spinbox.textarea -sticky nsew + } + + } + null -side right -sticky nsew -children { + Spinbox.uparrow -side left -sticky nsew + Spinbox.downarrow -side right -sticky nsew + } + } + + ttk::style layout Card.TFrame { + Card.field { + Card.padding -expand 1 + } + } + + ttk::style layout TLabelframe { + Labelframe.border { + Labelframe.padding -expand 1 -children { + Labelframe.label -side left + } + } + } + + ttk::style layout TNotebook { + Notebook.border -children { + TNotebook.Tab -expand 1 + Notebook.client -sticky nsew + } + } + + ttk::style layout Treeview.Item { + Treeitem.padding -sticky nsew -children { + Treeitem.image -side left -sticky {} + Treeitem.indicator -side left -sticky {} + Treeitem.text -side left -sticky {} + } + } + + # Button + ttk::style configure TButton -padding {8 4} -anchor center -foreground $colors(-fg) + + ttk::style map TButton -foreground \ + [list disabled #a2a2a2 \ + pressed #636363 \ + active #1a1a1a] + + ttk::style element create Button.button image \ + [list $images(button-rest) \ + {selected disabled} $images(button-disabled) \ + disabled $images(button-disabled) \ + selected $images(button-rest) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + # Toolbutton + ttk::style configure Toolbutton -padding {8 4} -anchor center + + ttk::style element create Toolbutton.button image \ + [list $images(empty) \ + {selected disabled} $images(button-disabled) \ + selected $images(button-rest) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + # Menubutton + ttk::style configure TMenubutton -padding {8 4 0 4} + + ttk::style element create Menubutton.button \ + image [list $images(button-rest) \ + disabled $images(button-disabled) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + ttk::style element create Menubutton.indicator image $images(arrow-down) -width 28 -sticky {} + + # OptionMenu + ttk::style configure TOptionMenu -padding {8 4 0 4} + + ttk::style element create OptionMenu.button \ + image [list $images(button-rest) \ + disabled $images(button-disabled) \ + pressed $images(button-pressed) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + ttk::style element create OptionMenu.indicator image $images(arrow-down) -width 28 -sticky {} + + # Accent.TButton + ttk::style configure Accent.TButton -padding {8 4} -anchor center -foreground #ffffff + + ttk::style map Accent.TButton -foreground \ + [list disabled #ffffff \ + pressed #c1d8ee] + + ttk::style element create AccentButton.button image \ + [list $images(button-accent-rest) \ + {selected disabled} $images(button-accent-disabled) \ + disabled $images(button-accent-disabled) \ + selected $images(button-accent-rest) \ + pressed $images(button-accent-pressed) \ + active $images(button-accent-hover) \ + ] -border 4 -sticky nsew + + # Titlebar.TButton + ttk::style configure Titlebar.TButton -padding {8 4} -anchor center -foreground #1a1a1a + + ttk::style map Titlebar.TButton -foreground \ + [list disabled #a0a0a0 \ + pressed #606060 \ + active #191919] + + ttk::style element create TitlebarButton.button image \ + [list $images(empty) \ + disabled $images(empty) \ + pressed $images(button-titlebar-pressed) \ + active $images(button-titlebar-hover) \ + ] -border 4 -sticky nsew + + # Close.Titlebar.TButton + ttk::style configure Close.Titlebar.TButton -padding {8 4} -anchor center -foreground #1a1a1a + + ttk::style map Close.Titlebar.TButton -foreground \ + [list disabled #a0a0a0 \ + pressed #efc6c2 \ + active #ffffff] + + ttk::style element create CloseButton.button image \ + [list $images(empty) \ + disabled $images(empty) \ + pressed $images(button-close-pressed) \ + active $images(button-close-hover) \ + ] -border 4 -sticky nsew + + # Checkbutton + ttk::style configure TCheckbutton -padding 4 + + ttk::style element create Checkbutton.indicator image \ + [list $images(check-unsel-rest) \ + {alternate disabled} $images(check-tri-disabled) \ + {selected disabled} $images(check-disabled) \ + disabled $images(check-unsel-disabled) \ + {pressed alternate} $images(check-tri-hover) \ + {active alternate} $images(check-tri-hover) \ + alternate $images(check-tri-rest) \ + {pressed selected} $images(check-hover) \ + {active selected} $images(check-hover) \ + selected $images(check-rest) \ + {pressed !selected} $images(check-unsel-pressed) \ + active $images(check-unsel-hover) \ + ] -width 26 -sticky w + + # Switch.TCheckbutton + ttk::style element create Switch.indicator image \ + [list $images(switch-off-rest) \ + {selected disabled} $images(switch-on-disabled) \ + disabled $images(switch-off-disabled) \ + {pressed selected} $images(switch-on-pressed) \ + {active selected} $images(switch-on-hover) \ + selected $images(switch-on-rest) \ + {pressed !selected} $images(switch-off-pressed) \ + active $images(switch-off-hover) \ + ] -width 46 -sticky w + + # Toggle.TButton + ttk::style configure Toggle.TButton -padding {8 4 8 4} -anchor center -foreground $colors(-fg) + + ttk::style map Toggle.TButton -foreground \ + [list {selected disabled} #ffffff \ + {selected pressed} #636363 \ + selected #ffffff \ + pressed #c1d8ee \ + disabled #a2a2a2 \ + active #1a1a1a + ] + + ttk::style element create ToggleButton.button image \ + [list $images(button-rest) \ + {selected disabled} $images(button-accent-disabled) \ + disabled $images(button-disabled) \ + {pressed selected} $images(button-rest) \ + {active selected} $images(button-accent-hover) \ + selected $images(button-accent-rest) \ + {pressed !selected} $images(button-accent-rest) \ + active $images(button-hover) \ + ] -border 4 -sticky nsew + + # Radiobutton + ttk::style configure TRadiobutton -padding 4 + + ttk::style element create Radiobutton.indicator image \ + [list $images(radio-unsel-rest) \ + {selected disabled} $images(radio-disabled) \ + disabled $images(radio-unsel-disabled) \ + {pressed selected} $images(radio-pressed) \ + {active selected} $images(radio-hover) \ + selected $images(radio-rest) \ + {pressed !selected} $images(radio-unsel-pressed) \ + active $images(radio-unsel-hover) \ + ] -width 26 -sticky w + + # Scrollbar + ttk::style element create Horizontal.Scrollbar.trough image $images(scroll-hor-trough) -sticky ew -border 6 + ttk::style element create Horizontal.Scrollbar.thumb image $images(scroll-hor-thumb) -sticky ew -border 3 + + ttk::style element create Horizontal.Scrollbar.rightarrow image $images(scroll-right) -sticky {} -width 12 + ttk::style element create Horizontal.Scrollbar.leftarrow image $images(scroll-left) -sticky {} -width 12 + + ttk::style element create Vertical.Scrollbar.trough image $images(scroll-vert-trough) -sticky ns -border 6 + ttk::style element create Vertical.Scrollbar.thumb image $images(scroll-vert-thumb) -sticky ns -border 3 + + ttk::style element create Vertical.Scrollbar.uparrow image $images(scroll-up) -sticky {} -height 12 + ttk::style element create Vertical.Scrollbar.downarrow image $images(scroll-down) -sticky {} -height 12 + + # Scale + ttk::style element create Horizontal.Scale.trough image $images(scale-trough-hor) \ + -border 5 -padding 0 + + ttk::style element create Vertical.Scale.trough image $images(scale-trough-vert) \ + -border 5 -padding 0 + + ttk::style element create Scale.slider \ + image [list $images(scale-thumb-rest) \ + disabled $images(scale-thumb-disabled) \ + pressed $images(scale-thumb-pressed) \ + active $images(scale-thumb-hover) \ + ] -sticky {} + + # Progressbar + ttk::style element create Horizontal.Progressbar.trough image $images(progress-trough-hor) \ + -border 1 -sticky ew + + ttk::style element create Horizontal.Progressbar.pbar image $images(progress-pbar-hor) \ + -border 2 -sticky ew + + ttk::style element create Vertical.Progressbar.trough image $images(progress-trough-vert) \ + -border 1 -sticky ns + + ttk::style element create Vertical.Progressbar.pbar image $images(progress-pbar-vert) \ + -border 2 -sticky ns + + # Entry + ttk::style configure TEntry -foreground $colors(-fg) + + ttk::style map TEntry -foreground \ + [list disabled #0a0a0a \ + pressed #636363 \ + active #626262 + ] + + ttk::style element create Entry.field \ + image [list $images(entry-rest) \ + {focus hover !invalid} $images(entry-focus) \ + invalid $images(entry-invalid) \ + disabled $images(entry-disabled) \ + {focus !invalid} $images(entry-focus) \ + hover $images(entry-hover) \ + ] -border 5 -padding 8 -sticky nsew + + # Combobox + ttk::style configure TCombobox -foreground $colors(-fg) + + ttk::style configure ComboboxPopdownFrame -borderwidth 1 -relief solid + + ttk::style map TCombobox -foreground \ + [list disabled #0a0a0a \ + pressed #636363 \ + active #626262 + ] + + ttk::style map TCombobox -selectbackground [list \ + {readonly hover} $colors(-selectbg) \ + {readonly focus} $colors(-selectbg) \ + ] -selectforeground [list \ + {readonly hover} $colors(-selectfg) \ + {readonly focus} $colors(-selectfg) \ + ] + + ttk::style element create Combobox.field \ + image [list $images(entry-rest) \ + {readonly disabled} $images(button-disabled) \ + {readonly pressed} $images(button-pressed) \ + {readonly hover} $images(button-hover) \ + readonly $images(button-rest) \ + invalid $images(entry-invalid) \ + disabled $images(entry-disabled) \ + focus $images(entry-focus) \ + hover $images(entry-hover) \ + ] -border 5 -padding {8 8 28 8} + + ttk::style element create Combobox.arrow image $images(arrow-down) -width 35 -sticky {} + + # Spinbox + ttk::style configure TSpinbox -foreground $colors(-fg) + + ttk::style map TSpinbox -foreground \ + [list disabled #0a0a0a \ + pressed #636363 \ + active #626262 + ] + + ttk::style element create Spinbox.field \ + image [list $images(entry-rest) \ + invalid $images(entry-invalid) \ + disabled $images(entry-disabled) \ + focus $images(entry-focus) \ + hover $images(entry-hover) \ + ] -border 5 -padding {8 8 54 8} -sticky nsew + + ttk::style element create Spinbox.uparrow image $images(arrow-up) -width 35 -sticky {} + ttk::style element create Spinbox.downarrow image $images(arrow-down) -width 35 -sticky {} + + # Sizegrip + ttk::style element create Sizegrip.sizegrip image $images(sizegrip) \ + -sticky nsew + + # Separator + ttk::style element create TSeparator.separator image $images(separator) + + # Card + ttk::style element create Card.field image $images(card) \ + -border 10 -padding 4 -sticky nsew + + # Labelframe + ttk::style element create Labelframe.border image $images(card) \ + -border 5 -padding 4 -sticky nsew + + # Notebook + ttk::style configure TNotebook -padding 1 + + ttk::style element create Notebook.border \ + image $images(notebook-border) -border 5 -padding 5 + + ttk::style element create Notebook.client image $images(notebook) + + ttk::style element create Notebook.tab \ + image [list $images(tab-rest) \ + selected $images(tab-selected) \ + active $images(tab-hover) \ + ] -border 13 -padding {16 14 16 6} -height 32 + + # Treeview + ttk::style element create Treeview.field image $images(card) \ + -border 5 + + ttk::style element create Treeheading.cell \ + image [list $images(treeheading-rest) \ + pressed $images(treeheading-pressed) \ + active $images(treeheading-hover) + ] -border 5 -padding 15 -sticky nsew + + ttk::style element create Treeitem.indicator \ + image [list $images(arrow-right) \ + user2 $images(empty) \ + user1 $images(arrow-down) \ + ] -width 26 -sticky {} + + ttk::style configure Treeview -foregound #1a1a1a -background $colors(-bg) -rowheight [expr {[font metrics font -linespace] + 2}] + ttk::style map Treeview \ + -background [list selected #f0f0f0] \ + -foreground [list selected #191919] + + # Panedwindow + # Insane hack to remove clam's ugly sash + ttk::style configure Sash -gripcount 0 + } +} \ No newline at end of file diff --git a/vmcompact/sun-valley-theme/theme/light/arrow-down.png b/vmcompact/sun-valley-theme/theme/light/arrow-down.png new file mode 100644 index 0000000..45fc33b Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/arrow-down.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/arrow-right.png b/vmcompact/sun-valley-theme/theme/light/arrow-right.png new file mode 100644 index 0000000..6461ffc Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/arrow-right.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/arrow-up.png b/vmcompact/sun-valley-theme/theme/light/arrow-up.png new file mode 100644 index 0000000..4dd379f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/arrow-up.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-accent-disabled.png b/vmcompact/sun-valley-theme/theme/light/button-accent-disabled.png new file mode 100644 index 0000000..c3845a5 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-accent-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-accent-hover.png b/vmcompact/sun-valley-theme/theme/light/button-accent-hover.png new file mode 100644 index 0000000..054d56c Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-accent-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-accent-pressed.png b/vmcompact/sun-valley-theme/theme/light/button-accent-pressed.png new file mode 100644 index 0000000..9da8b53 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-accent-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-accent-rest.png b/vmcompact/sun-valley-theme/theme/light/button-accent-rest.png new file mode 100644 index 0000000..3b7959a Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-accent-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-close-hover.png b/vmcompact/sun-valley-theme/theme/light/button-close-hover.png new file mode 100644 index 0000000..f331cf2 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-close-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-close-pressed.png b/vmcompact/sun-valley-theme/theme/light/button-close-pressed.png new file mode 100644 index 0000000..d0bff3f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-close-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-disabled.png b/vmcompact/sun-valley-theme/theme/light/button-disabled.png new file mode 100644 index 0000000..aef75fa Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-hover.png b/vmcompact/sun-valley-theme/theme/light/button-hover.png new file mode 100644 index 0000000..53a381f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-pressed.png b/vmcompact/sun-valley-theme/theme/light/button-pressed.png new file mode 100644 index 0000000..920bf70 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-rest.png b/vmcompact/sun-valley-theme/theme/light/button-rest.png new file mode 100644 index 0000000..1b21188 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-titlebar-hover.png b/vmcompact/sun-valley-theme/theme/light/button-titlebar-hover.png new file mode 100644 index 0000000..3f0586a Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-titlebar-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/button-titlebar-pressed.png b/vmcompact/sun-valley-theme/theme/light/button-titlebar-pressed.png new file mode 100644 index 0000000..e0a5f10 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/button-titlebar-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/card.png b/vmcompact/sun-valley-theme/theme/light/card.png new file mode 100644 index 0000000..78ac82e Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/card.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-disabled.png b/vmcompact/sun-valley-theme/theme/light/check-disabled.png new file mode 100644 index 0000000..2c59e08 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-hover.png b/vmcompact/sun-valley-theme/theme/light/check-hover.png new file mode 100644 index 0000000..a104363 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-pressed.png b/vmcompact/sun-valley-theme/theme/light/check-pressed.png new file mode 100644 index 0000000..63e91e0 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-rest.png b/vmcompact/sun-valley-theme/theme/light/check-rest.png new file mode 100644 index 0000000..4f8d140 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-tri-disabled.png b/vmcompact/sun-valley-theme/theme/light/check-tri-disabled.png new file mode 100644 index 0000000..5c796c0 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-tri-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-tri-hover.png b/vmcompact/sun-valley-theme/theme/light/check-tri-hover.png new file mode 100644 index 0000000..a11cd66 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-tri-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-tri-pressed.png b/vmcompact/sun-valley-theme/theme/light/check-tri-pressed.png new file mode 100644 index 0000000..af79f7f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-tri-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-tri-rest.png b/vmcompact/sun-valley-theme/theme/light/check-tri-rest.png new file mode 100644 index 0000000..a9c3168 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-tri-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-unsel-disabled.png b/vmcompact/sun-valley-theme/theme/light/check-unsel-disabled.png new file mode 100644 index 0000000..a0f3132 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-unsel-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-unsel-hover.png b/vmcompact/sun-valley-theme/theme/light/check-unsel-hover.png new file mode 100644 index 0000000..941817c Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-unsel-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-unsel-pressed.png b/vmcompact/sun-valley-theme/theme/light/check-unsel-pressed.png new file mode 100644 index 0000000..a31a6c5 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-unsel-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/check-unsel-rest.png b/vmcompact/sun-valley-theme/theme/light/check-unsel-rest.png new file mode 100644 index 0000000..3248269 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/check-unsel-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/empty.png b/vmcompact/sun-valley-theme/theme/light/empty.png new file mode 100644 index 0000000..2218363 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/empty.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/entry-disabled.png b/vmcompact/sun-valley-theme/theme/light/entry-disabled.png new file mode 100644 index 0000000..920bf70 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/entry-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/entry-focus.png b/vmcompact/sun-valley-theme/theme/light/entry-focus.png new file mode 100644 index 0000000..5630902 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/entry-focus.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/entry-hover.png b/vmcompact/sun-valley-theme/theme/light/entry-hover.png new file mode 100644 index 0000000..9ad7d53 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/entry-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/entry-invalid.png b/vmcompact/sun-valley-theme/theme/light/entry-invalid.png new file mode 100644 index 0000000..cc73c41 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/entry-invalid.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/entry-rest.png b/vmcompact/sun-valley-theme/theme/light/entry-rest.png new file mode 100644 index 0000000..d347a65 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/entry-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/notebook-border.png b/vmcompact/sun-valley-theme/theme/light/notebook-border.png new file mode 100644 index 0000000..5d06b01 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/notebook-border.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/notebook.png b/vmcompact/sun-valley-theme/theme/light/notebook.png new file mode 100644 index 0000000..255dee8 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/notebook.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/progress-pbar-hor.png b/vmcompact/sun-valley-theme/theme/light/progress-pbar-hor.png new file mode 100644 index 0000000..9806e3d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/progress-pbar-hor.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/progress-pbar-vert.png b/vmcompact/sun-valley-theme/theme/light/progress-pbar-vert.png new file mode 100644 index 0000000..85738be Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/progress-pbar-vert.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/progress-trough-hor.png b/vmcompact/sun-valley-theme/theme/light/progress-trough-hor.png new file mode 100644 index 0000000..6999a37 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/progress-trough-hor.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/progress-trough-vert.png b/vmcompact/sun-valley-theme/theme/light/progress-trough-vert.png new file mode 100644 index 0000000..2d84875 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/progress-trough-vert.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-disabled.png b/vmcompact/sun-valley-theme/theme/light/radio-disabled.png new file mode 100644 index 0000000..d44a9bf Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-hover.png b/vmcompact/sun-valley-theme/theme/light/radio-hover.png new file mode 100644 index 0000000..af45ede Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-pressed.png b/vmcompact/sun-valley-theme/theme/light/radio-pressed.png new file mode 100644 index 0000000..aaf1999 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-rest.png b/vmcompact/sun-valley-theme/theme/light/radio-rest.png new file mode 100644 index 0000000..d6967e1 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-unsel-disabled.png b/vmcompact/sun-valley-theme/theme/light/radio-unsel-disabled.png new file mode 100644 index 0000000..2fbffcf Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-unsel-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-unsel-hover.png b/vmcompact/sun-valley-theme/theme/light/radio-unsel-hover.png new file mode 100644 index 0000000..7abe53e Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-unsel-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-unsel-pressed.png b/vmcompact/sun-valley-theme/theme/light/radio-unsel-pressed.png new file mode 100644 index 0000000..107afef Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-unsel-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/radio-unsel-rest.png b/vmcompact/sun-valley-theme/theme/light/radio-unsel-rest.png new file mode 100644 index 0000000..8dda1f2 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/radio-unsel-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scale-thumb-disabled.png b/vmcompact/sun-valley-theme/theme/light/scale-thumb-disabled.png new file mode 100644 index 0000000..3fa79f4 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scale-thumb-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scale-thumb-hover.png b/vmcompact/sun-valley-theme/theme/light/scale-thumb-hover.png new file mode 100644 index 0000000..34664b4 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scale-thumb-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scale-thumb-pressed.png b/vmcompact/sun-valley-theme/theme/light/scale-thumb-pressed.png new file mode 100644 index 0000000..b0de0d0 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scale-thumb-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scale-thumb-rest.png b/vmcompact/sun-valley-theme/theme/light/scale-thumb-rest.png new file mode 100644 index 0000000..46bd9ed Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scale-thumb-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scale-trough-hor.png b/vmcompact/sun-valley-theme/theme/light/scale-trough-hor.png new file mode 100644 index 0000000..7adbe2d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scale-trough-hor.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scale-trough-vert.png b/vmcompact/sun-valley-theme/theme/light/scale-trough-vert.png new file mode 100644 index 0000000..924dfa9 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scale-trough-vert.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-down.png b/vmcompact/sun-valley-theme/theme/light/scroll-down.png new file mode 100644 index 0000000..f4dd741 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-down.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-hor-thumb.png b/vmcompact/sun-valley-theme/theme/light/scroll-hor-thumb.png new file mode 100644 index 0000000..989bc94 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-hor-thumb.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-hor-trough.png b/vmcompact/sun-valley-theme/theme/light/scroll-hor-trough.png new file mode 100644 index 0000000..afeae8c Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-hor-trough.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-left.png b/vmcompact/sun-valley-theme/theme/light/scroll-left.png new file mode 100644 index 0000000..498d3ca Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-left.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-right.png b/vmcompact/sun-valley-theme/theme/light/scroll-right.png new file mode 100644 index 0000000..7f771bf Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-right.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-up.png b/vmcompact/sun-valley-theme/theme/light/scroll-up.png new file mode 100644 index 0000000..09ef917 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-up.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-vert-thumb.png b/vmcompact/sun-valley-theme/theme/light/scroll-vert-thumb.png new file mode 100644 index 0000000..6f84abf Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-vert-thumb.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/scroll-vert-trough.png b/vmcompact/sun-valley-theme/theme/light/scroll-vert-trough.png new file mode 100644 index 0000000..175bb6e Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/scroll-vert-trough.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/separator.png b/vmcompact/sun-valley-theme/theme/light/separator.png new file mode 100644 index 0000000..1e7b972 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/separator.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/sizegrip.png b/vmcompact/sun-valley-theme/theme/light/sizegrip.png new file mode 100644 index 0000000..bbcdc5f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/sizegrip.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-off-disabled.png b/vmcompact/sun-valley-theme/theme/light/switch-off-disabled.png new file mode 100644 index 0000000..56a098f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-off-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-off-hover.png b/vmcompact/sun-valley-theme/theme/light/switch-off-hover.png new file mode 100644 index 0000000..2af2b43 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-off-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-off-pressed.png b/vmcompact/sun-valley-theme/theme/light/switch-off-pressed.png new file mode 100644 index 0000000..d5fef56 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-off-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-off-rest.png b/vmcompact/sun-valley-theme/theme/light/switch-off-rest.png new file mode 100644 index 0000000..d996bcc Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-off-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-on-disabled.png b/vmcompact/sun-valley-theme/theme/light/switch-on-disabled.png new file mode 100644 index 0000000..3d03bc9 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-on-disabled.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-on-hover.png b/vmcompact/sun-valley-theme/theme/light/switch-on-hover.png new file mode 100644 index 0000000..24eb9f7 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-on-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-on-pressed.png b/vmcompact/sun-valley-theme/theme/light/switch-on-pressed.png new file mode 100644 index 0000000..6418536 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-on-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/switch-on-rest.png b/vmcompact/sun-valley-theme/theme/light/switch-on-rest.png new file mode 100644 index 0000000..bf85044 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/switch-on-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/tab-hover.png b/vmcompact/sun-valley-theme/theme/light/tab-hover.png new file mode 100644 index 0000000..0c6df3e Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/tab-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/tab-rest.png b/vmcompact/sun-valley-theme/theme/light/tab-rest.png new file mode 100644 index 0000000..725beb9 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/tab-rest.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/tab-selected.png b/vmcompact/sun-valley-theme/theme/light/tab-selected.png new file mode 100644 index 0000000..c030177 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/tab-selected.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/treeheading-hover.png b/vmcompact/sun-valley-theme/theme/light/treeheading-hover.png new file mode 100644 index 0000000..47bf56f Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/treeheading-hover.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/treeheading-pressed.png b/vmcompact/sun-valley-theme/theme/light/treeheading-pressed.png new file mode 100644 index 0000000..089056d Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/treeheading-pressed.png differ diff --git a/vmcompact/sun-valley-theme/theme/light/treeheading-rest.png b/vmcompact/sun-valley-theme/theme/light/treeheading-rest.png new file mode 100644 index 0000000..d4aa095 Binary files /dev/null and b/vmcompact/sun-valley-theme/theme/light/treeheading-rest.png differ