Remodeling it to use builder
This commit is contained in:
parent
27dd9da6a6
commit
620018c4c7
12 changed files with 196 additions and 109 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,3 +9,5 @@ pkg/
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
src/recoder.egg-info/
|
src/recoder.egg-info/
|
||||||
|
|
||||||
|
*.gresource
|
||||||
|
|
14
PKGBUILD
14
PKGBUILD
|
@ -1,4 +1,4 @@
|
||||||
# Maintainer: Jeena <your-email@example.com>
|
# Maintainer: Jeena <hello@jeena.net>
|
||||||
pkgname=recoder
|
pkgname=recoder
|
||||||
pkgver=1.0.0
|
pkgver=1.0.0
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
|
@ -16,27 +16,31 @@ depends=(
|
||||||
)
|
)
|
||||||
optdepends=(
|
optdepends=(
|
||||||
'libcanberra: play system notification sounds'
|
'libcanberra: play system notification sounds'
|
||||||
'sound-theme-freedesktop: standard system sounds like \"complete.oga\"'
|
'sound-theme-freedesktop: standard system sounds like "complete.oga"'
|
||||||
)
|
)
|
||||||
makedepends=(
|
makedepends=(
|
||||||
'python-setuptools'
|
'python-setuptools'
|
||||||
'python-build'
|
'python-build'
|
||||||
'python-installer'
|
'python-installer'
|
||||||
|
'glib2'
|
||||||
)
|
)
|
||||||
source=()
|
source=()
|
||||||
sha256sums=()
|
sha256sums=()
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/../" # go to your project root
|
cd "$srcdir/../" # go to your project root
|
||||||
|
glib-compile-resources src/resources/resources.xml \
|
||||||
|
--target=src/recoder/resources.gresource \
|
||||||
|
--sourcedir=src/resources
|
||||||
python -m build --wheel --outdir dist
|
python -m build --wheel --outdir dist
|
||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/../" # back to project root where dist/ is
|
cd "$srcdir/../"
|
||||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
python -m installer --destdir="$pkgdir" dist/*.whl
|
||||||
|
|
||||||
install -Dm644 resources/net.jeena.Recoder.desktop \
|
install -Dm644 src/resources/net.jeena.Recoder.desktop \
|
||||||
"$pkgdir/usr/share/applications/net.jeena.Recoder.desktop"
|
"$pkgdir/usr/share/applications/net.jeena.Recoder.desktop"
|
||||||
install -Dm644 resources/net.jeena.Recoder.png \
|
install -Dm644 src/resources/net.jeena.Recoder.png \
|
||||||
"$pkgdir/usr/share/icons/hicolor/256x256/apps/net.jeena.Recoder.png"
|
"$pkgdir/usr/share/icons/hicolor/256x256/apps/net.jeena.Recoder.png"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
gi.require_version('Gtk', '4.0')
|
gi.require_version('Gtk', '4.0')
|
||||||
|
@ -9,7 +10,14 @@ from gi.repository import Adw, Gio
|
||||||
|
|
||||||
Adw.init()
|
Adw.init()
|
||||||
|
|
||||||
|
def load_resources():
|
||||||
|
resource_path = os.path.join(os.path.dirname(__file__), "../resources/resources.gresource")
|
||||||
|
resource = Gio.Resource.load(resource_path)
|
||||||
|
Gio.resources_register(resource)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
load_resources()
|
||||||
|
|
||||||
from recoder.ui import RecoderWindow # delayed import
|
from recoder.ui import RecoderWindow # delayed import
|
||||||
|
|
||||||
class RecoderApp(Adw.Application):
|
class RecoderApp(Adw.Application):
|
||||||
|
@ -21,7 +29,7 @@ def main():
|
||||||
def do_activate(self):
|
def do_activate(self):
|
||||||
if not self.window:
|
if not self.window:
|
||||||
self.window = RecoderWindow(application=self)
|
self.window = RecoderWindow(application=self)
|
||||||
self.window.present()
|
self.window.window.present()
|
||||||
|
|
||||||
app = RecoderApp()
|
app = RecoderApp()
|
||||||
return app.run(sys.argv)
|
return app.run(sys.argv)
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
from gi.repository import GLib
|
|
||||||
|
|
||||||
CONFIG_PATH = Path(GLib.get_user_config_dir()) / "recoder" / "config.json"
|
|
||||||
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
def save_config(config):
|
|
||||||
with open(CONFIG_PATH, "w") as f:
|
|
||||||
json.dump(config, f)
|
|
||||||
|
|
||||||
def load_config():
|
|
||||||
if CONFIG_PATH.exists():
|
|
||||||
with open(CONFIG_PATH) as f:
|
|
||||||
return json.load(f)
|
|
||||||
return {}
|
|
|
@ -7,10 +7,11 @@ from gi.repository import GLib
|
||||||
class TranscoderWorker:
|
class TranscoderWorker:
|
||||||
TIME_RE = re.compile(r"time=(\d+):(\d+):(\d+)\.(\d+)")
|
TIME_RE = re.compile(r"time=(\d+):(\d+):(\d+)\.(\d+)")
|
||||||
|
|
||||||
def __init__(self, files, progress_callback=None, done_callback=None):
|
def __init__(self, files, progress_callback=None, done_callback=None, file_done_callback=None):
|
||||||
self.files = files
|
self.files = files
|
||||||
self.progress_callback = progress_callback
|
self.progress_callback = progress_callback
|
||||||
self.done_callback = done_callback
|
self.done_callback = done_callback
|
||||||
|
self.file_done_callback = file_done_callback
|
||||||
self.is_processing = False
|
self.is_processing = False
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
@ -27,12 +28,21 @@ class TranscoderWorker:
|
||||||
basename = os.path.basename(filepath)
|
basename = os.path.basename(filepath)
|
||||||
self._update_progress(f"Starting {basename}", (idx - 1) / total)
|
self._update_progress(f"Starting {basename}", (idx - 1) / total)
|
||||||
|
|
||||||
success, output_path = self._transcode_file(filepath, os.path.join(os.path.dirname(filepath), "transcoded"), basename, idx, total)
|
success, output_path = self._transcode_file(
|
||||||
|
filepath,
|
||||||
|
os.path.join(os.path.dirname(filepath), "transcoded"),
|
||||||
|
basename,
|
||||||
|
idx,
|
||||||
|
total,
|
||||||
|
)
|
||||||
if not success:
|
if not success:
|
||||||
self._update_progress(f"Error transcoding {basename}", idx / total)
|
self._update_progress(f"Error transcoding {basename}", idx / total)
|
||||||
else:
|
else:
|
||||||
self._update_progress(f"Finished {basename}", idx / total)
|
self._update_progress(f"Finished {basename}", idx / total)
|
||||||
|
|
||||||
|
if self.file_done_callback:
|
||||||
|
GLib.idle_add(self.file_done_callback, filepath)
|
||||||
|
|
||||||
self.is_processing = False
|
self.is_processing = False
|
||||||
self._update_progress("All done!", 1.0)
|
self._update_progress("All done!", 1.0)
|
||||||
self._notify_done()
|
self._notify_done()
|
||||||
|
@ -47,9 +57,17 @@ class TranscoderWorker:
|
||||||
duration = 1.0 # fallback to avoid division by zero
|
duration = 1.0 # fallback to avoid division by zero
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
"ffmpeg", "-y", "-i", input_path,
|
"ffmpeg",
|
||||||
"-c:v", "libx264", "-preset", "fast",
|
"-y",
|
||||||
"-c:a", "aac", output_path
|
"-i",
|
||||||
|
input_path,
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-preset",
|
||||||
|
"fast",
|
||||||
|
"-c:a",
|
||||||
|
"aac",
|
||||||
|
output_path,
|
||||||
]
|
]
|
||||||
|
|
||||||
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
|
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
|
||||||
|
@ -68,16 +86,19 @@ class TranscoderWorker:
|
||||||
self._update_progress(f"Transcoding {basename}", overall_fraction)
|
self._update_progress(f"Transcoding {basename}", overall_fraction)
|
||||||
|
|
||||||
process.wait()
|
process.wait()
|
||||||
success = (process.returncode == 0)
|
success = process.returncode == 0
|
||||||
return success, output_path
|
return success, output_path
|
||||||
|
|
||||||
def _get_duration(self, input_path):
|
def _get_duration(self, input_path):
|
||||||
# Run ffprobe to get duration in seconds
|
|
||||||
cmd = [
|
cmd = [
|
||||||
"ffprobe", "-v", "error",
|
"ffprobe",
|
||||||
"-show_entries", "format=duration",
|
"-v",
|
||||||
"-of", "default=noprint_wrappers=1:nokey=1",
|
"error",
|
||||||
input_path
|
"-show_entries",
|
||||||
|
"format=duration",
|
||||||
|
"-of",
|
||||||
|
"default=noprint_wrappers=1:nokey=1",
|
||||||
|
input_path,
|
||||||
]
|
]
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(cmd, universal_newlines=True)
|
output = subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
|
|
@ -8,104 +8,75 @@ gi.require_version('Notify', '0.7')
|
||||||
from gi.repository import Gtk, Adw, Gio, Gdk, GLib, Notify
|
from gi.repository import Gtk, Adw, Gio, Gdk, GLib, Notify
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from recoder.config import load_config, save_config
|
|
||||||
from recoder.transcoder_worker import TranscoderWorker
|
from recoder.transcoder_worker import TranscoderWorker
|
||||||
|
|
||||||
class RecoderWindow(Adw.ApplicationWindow):
|
class FileEntryRow(Gtk.ListBoxRow):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, path):
|
||||||
super().__init__(**kwargs)
|
super().__init__()
|
||||||
self.set_title("Recoder")
|
self.path = path
|
||||||
self.set_default_size(700, 400)
|
self.icon = Gtk.Image.new_from_icon_name("media-playback-pause-symbolic")
|
||||||
|
self.label = Gtk.Label(label=os.path.basename(path), xalign=0)
|
||||||
|
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
|
||||||
|
hbox.append(self.icon)
|
||||||
|
hbox.append(self.label)
|
||||||
|
self.set_child(hbox)
|
||||||
|
|
||||||
self.config = load_config()
|
def mark_done(self):
|
||||||
self.current_dir = self.config.get("last_directory", str(os.path.expanduser("~")))
|
self.icon.set_from_icon_name("emblem-ok-symbolic")
|
||||||
|
self.label.set_text(f"{os.path.basename(self.path)} - Done")
|
||||||
|
|
||||||
|
class RecoderWindow():
|
||||||
|
|
||||||
|
def __init__(self, application, **kwargs):
|
||||||
|
|
||||||
self.files_to_process = []
|
self.files_to_process = []
|
||||||
self.transcoder = None
|
self.transcoder = None
|
||||||
|
self.file_rows = {}
|
||||||
|
|
||||||
# UI setup
|
# Load UI from resource
|
||||||
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
builder = Gtk.Builder()
|
||||||
self.header_bar = Adw.HeaderBar()
|
builder.add_from_resource("/net/jeena/recoder/recoder.ui")
|
||||||
self.toolbar_view = Adw.ToolbarView()
|
|
||||||
self.toolbar_view.add_top_bar(self.header_bar)
|
|
||||||
self.toolbar_view.set_content(self.vbox)
|
|
||||||
self.set_content(self.toolbar_view)
|
|
||||||
|
|
||||||
self.textview = Gtk.TextView()
|
self.window = builder.get_object("main_window")
|
||||||
self.textview.set_editable(False)
|
self.window.set_application(application)
|
||||||
self.textbuffer = self.textview.get_buffer()
|
|
||||||
|
self.overlay = builder.get_object("overlay")
|
||||||
|
self.progress = builder.get_object("progress_bar")
|
||||||
|
self.drop_hint = builder.get_object("drop_hint")
|
||||||
|
self.listbox = builder.get_object("listbox")
|
||||||
|
self.scrolled_window = builder.get_object("scrolled_window")
|
||||||
|
|
||||||
self.drop_target = Gtk.DropTarget.new(Gio.File, Gdk.DragAction.COPY)
|
self.drop_target = Gtk.DropTarget.new(Gio.File, Gdk.DragAction.COPY)
|
||||||
|
self.drop_target.connect("drop", self.on_drop)
|
||||||
self.drop_target.connect("enter", self.on_drop_enter)
|
self.drop_target.connect("enter", self.on_drop_enter)
|
||||||
self.drop_target.connect("leave", self.on_drop_leave)
|
self.drop_target.connect("leave", self.on_drop_leave)
|
||||||
self.drop_target.connect("drop", self.on_drop)
|
|
||||||
self.textview.add_controller(self.drop_target)
|
|
||||||
|
|
||||||
# CSS
|
self.window.add_controller(self.drop_target)
|
||||||
self._setup_css()
|
|
||||||
|
|
||||||
self.drop_hint = Gtk.Label(label="📂 Drop files here to get started")
|
css_provider = Gtk.CssProvider()
|
||||||
self.drop_hint.add_css_class("dim-label")
|
css_provider.load_from_resource("/net/jeena/recoder/style.css")
|
||||||
self._setup_dim_label_css()
|
Gtk.StyleContext.add_provider_for_display(
|
||||||
|
Gdk.Display.get_default(),
|
||||||
overlay = Gtk.Overlay()
|
css_provider,
|
||||||
overlay.set_child(self.textview)
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||||
overlay.add_overlay(self.drop_hint)
|
)
|
||||||
self.drop_hint.set_halign(Gtk.Align.CENTER)
|
|
||||||
self.drop_hint.set_valign(Gtk.Align.CENTER)
|
|
||||||
|
|
||||||
self.scrolled = Gtk.ScrolledWindow()
|
|
||||||
self.scrolled.set_child(overlay)
|
|
||||||
self.vbox.append(self.scrolled)
|
|
||||||
self.scrolled.set_vexpand(True)
|
|
||||||
self.scrolled.set_hexpand(True)
|
|
||||||
|
|
||||||
self.progress = Gtk.ProgressBar()
|
|
||||||
self.vbox.append(self.progress)
|
|
||||||
self.progress.set_hexpand(True)
|
|
||||||
|
|
||||||
Notify.init("Recoder")
|
Notify.init("Recoder")
|
||||||
|
|
||||||
def _setup_css(self):
|
|
||||||
css = b"""
|
|
||||||
textview.drop-highlight {
|
|
||||||
background-color: @theme_selected_bg_color;
|
|
||||||
border: 2px solid @theme_selected_fg_color;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
style_provider = Gtk.CssProvider()
|
|
||||||
style_provider.load_from_data(css)
|
|
||||||
Gtk.StyleContext.add_provider_for_display(
|
|
||||||
Gdk.Display.get_default(),
|
|
||||||
style_provider,
|
|
||||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _setup_dim_label_css(self):
|
|
||||||
css = b"""
|
|
||||||
.dim-label {
|
|
||||||
font-size: 16px;
|
|
||||||
color: @theme_unfocused_fg_color;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
style_provider = Gtk.CssProvider()
|
|
||||||
style_provider.load_from_data(css)
|
|
||||||
Gtk.StyleContext.add_provider_for_display(
|
|
||||||
Gdk.Display.get_default(),
|
|
||||||
style_provider,
|
|
||||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_drop_enter(self, drop_target, x, y):
|
def on_drop_enter(self, drop_target, x, y):
|
||||||
self.textview.add_css_class("drop-highlight")
|
self.overlay.add_css_class("drop-highlight")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_drop_leave(self, drop_target):
|
def on_drop_leave(self, drop_target):
|
||||||
self.textview.remove_css_class("drop-highlight")
|
self.overlay.remove_css_class("drop-highlight")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_drop(self, drop_target, value, x, y):
|
def on_drop(self, drop_target, value, x, y):
|
||||||
GLib.idle_add(partial(self.process_drop_value, value))
|
GLib.idle_add(partial(self.process_drop_value, value))
|
||||||
|
self.overlay.remove_overlay(self.drop_hint)
|
||||||
|
self.progress.set_visible(True)
|
||||||
|
self.progress.set_fraction(0.0) # optionally reset
|
||||||
|
self.drop_hint.set_visible(False)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def process_drop_value(self, value):
|
def process_drop_value(self, value):
|
||||||
|
@ -129,10 +100,21 @@ class RecoderWindow(Adw.ApplicationWindow):
|
||||||
if not paths:
|
if not paths:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.textview.remove_css_class("drop-highlight")
|
# Clear previous listbox rows
|
||||||
self.drop_hint.set_visible(False)
|
children = self.listbox.get_first_child()
|
||||||
|
while children:
|
||||||
|
next_child = children.get_next_sibling()
|
||||||
|
self.listbox.remove(children)
|
||||||
|
children = next_child
|
||||||
|
self.file_rows.clear()
|
||||||
|
|
||||||
|
# Add new files to listbox with custom FileEntryRow
|
||||||
|
for path in paths:
|
||||||
|
row = FileEntryRow(path)
|
||||||
|
self.listbox.append(row)
|
||||||
|
self.file_rows[path] = row
|
||||||
|
|
||||||
self.files_to_process = paths
|
self.files_to_process = paths
|
||||||
self.textbuffer.set_text("\n".join(os.path.basename(p) for p in paths) + "\n")
|
|
||||||
self.start_transcoding()
|
self.start_transcoding()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -147,6 +129,7 @@ class RecoderWindow(Adw.ApplicationWindow):
|
||||||
self.files_to_process,
|
self.files_to_process,
|
||||||
progress_callback=self.update_progress,
|
progress_callback=self.update_progress,
|
||||||
done_callback=self.notify_done,
|
done_callback=self.notify_done,
|
||||||
|
file_done_callback=self.mark_file_done,
|
||||||
)
|
)
|
||||||
self.transcoder.start()
|
self.transcoder.start()
|
||||||
|
|
||||||
|
@ -155,6 +138,11 @@ class RecoderWindow(Adw.ApplicationWindow):
|
||||||
self.progress.set_text(text)
|
self.progress.set_text(text)
|
||||||
self.progress.set_fraction(fraction)
|
self.progress.set_fraction(fraction)
|
||||||
|
|
||||||
|
def mark_file_done(self, filepath):
|
||||||
|
if filepath in self.file_rows:
|
||||||
|
row = self.file_rows[filepath]
|
||||||
|
GLib.idle_add(row.mark_done)
|
||||||
|
|
||||||
def notify_done(self):
|
def notify_done(self):
|
||||||
self.progress.set_show_text(True)
|
self.progress.set_show_text(True)
|
||||||
self.progress.set_text("Transcoding Complete!")
|
self.progress.set_text("Transcoding Complete!")
|
||||||
|
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
61
src/resources/recoder.ui
Normal file
61
src/resources/recoder.ui
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk" version="4.0"/>
|
||||||
|
<requires lib="adw" version="1.0"/>
|
||||||
|
|
||||||
|
<object class="AdwApplicationWindow" id="main_window">
|
||||||
|
<property name="title">Recoder</property>
|
||||||
|
<property name="default-width">700</property>
|
||||||
|
<property name="default-height">400</property>
|
||||||
|
<property name="content">
|
||||||
|
<object class="AdwToolbarView" id="toolbar_view">
|
||||||
|
<child type="top">
|
||||||
|
<object class="AdwHeaderBar" id="header_bar">
|
||||||
|
<!-- You can add title or buttons here if needed -->
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<property name="content">
|
||||||
|
<object class="GtkBox" id="main_box">
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkOverlay" id="overlay">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="listbox">
|
||||||
|
<property name="selection-mode">none</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="overlay">
|
||||||
|
<object class="GtkLabel" id="drop_hint">
|
||||||
|
<property name="label">📂 Drop files here to get started</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<style>
|
||||||
|
<class name="dim-label"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkProgressBar" id="progress_bar">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<property name="visible">False</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</interface>
|
6
src/resources/resources.xml
Normal file
6
src/resources/resources.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<gresources>
|
||||||
|
<gresource prefix="/net/jeena/recoder">
|
||||||
|
<file>recoder.ui</file>
|
||||||
|
<file>style.css</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
13
src/resources/style.css
Normal file
13
src/resources/style.css
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
.drop-highlight {
|
||||||
|
background-color: @theme_selected_bg_color;
|
||||||
|
border: 2px solid @theme_selected_fg_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dim-label {
|
||||||
|
font-size: 16px;
|
||||||
|
color: @theme_unfocused_fg_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-row {
|
||||||
|
background: red;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue