Skip to content

Commit 2e72866

Browse files
committed
🔨 refactor: Added theme options for Gnome, Cinnamon, and XFCE.
1 parent 6b11c87 commit 2e72866

5 files changed

Lines changed: 350 additions & 61 deletions

File tree

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,7 @@ cython_debug/
161161

162162
# PyPI configuration file
163163
.pypirc
164+
165+
# Claude Code
166+
.claude
167+
CLAUDE.md

‎biglinux-livecd/usr/share/biglinux/livecd/config.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ class SetupConfig:
1515
keyboard_layout: Optional[str] = None
1616
desktop_layout: Optional[str] = None
1717
theme: Optional[str] = None
18+
simple_theme: Optional[str] = None # "light" or "dark" for simplified environments
1819
enable_jamesdsp: bool = False
1920
enable_enhanced_contrast: bool = False

‎biglinux-livecd/usr/share/biglinux/livecd/services.py‎

Lines changed: 192 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def __init__(self, test_mode: bool = False):
3838
self.tmp_theme_file = "/tmp/big_desktop_theme"
3939
self.tmp_jamesdsp_file = "/tmp/big_enable_jamesdsp"
4040
self.tmp_display_profile_file = "/tmp/big_improve_display"
41+
self.tmp_simple_theme_file = "/tmp/big_simple_theme"
4142

4243
def _run_command(
4344
self,
@@ -174,6 +175,167 @@ def apply_theme(self, theme: str):
174175
self._write_tmp_file(self.tmp_theme_file, theme)
175176
self._run_command([self.theme_apply_script, theme])
176177

178+
def apply_simple_theme(self, theme: str):
179+
"""
180+
Applies light or dark theme for simplified environments (GNOME/XFCE/Cinnamon).
181+
182+
Args:
183+
theme: Either "light" or "dark"
184+
"""
185+
logger.info(f"Applying simple theme: {theme}")
186+
self._write_tmp_file(self.tmp_simple_theme_file, theme)
187+
188+
desktop_env = self.get_desktop_environment()
189+
190+
if theme == "dark":
191+
self._apply_dark_theme(desktop_env)
192+
else:
193+
self._apply_light_theme(desktop_env)
194+
195+
def _apply_dark_theme(self, desktop_env: str):
196+
"""Applies dark theme configuration."""
197+
logger.info("Applying dark theme configuration")
198+
199+
# Set GTK4 color scheme for GNOME/Cinnamon
200+
if desktop_env in ["GNOME", "Cinnamon"]:
201+
self._run_command([
202+
"dconf", "write",
203+
"/org/gnome/desktop/interface/color-scheme",
204+
"'prefer-dark'"
205+
])
206+
207+
# Configure Kvantum theme
208+
home = os.path.expanduser("~")
209+
kvantum_dir = os.path.join(home, ".config", "Kvantum")
210+
kvantum_conf = os.path.join(kvantum_dir, "kvantum.kvconfig")
211+
212+
kvantum_content = "[General]\ntheme=BigAdwaitaRoundGtkDark\n"
213+
self._write_user_config_file(kvantum_conf, kvantum_content)
214+
215+
# Copy kdeglobals for dark theme
216+
kdeglobals_source = "/usr/share/sync-kde-and-gtk-places/biglinux-dark"
217+
kdeglobals_dest = os.path.join(home, ".config", "kdeglobals")
218+
if os.path.exists(kdeglobals_source):
219+
self._run_command(["cp", "-f", kdeglobals_source, kdeglobals_dest])
220+
else:
221+
logger.warning(f"Dark theme kdeglobals not found at {kdeglobals_source}")
222+
223+
# Apply dark icon theme
224+
self._apply_icon_theme_variant(desktop_env, dark=True)
225+
226+
def _apply_light_theme(self, desktop_env: str):
227+
"""Applies light theme configuration."""
228+
logger.info("Applying light theme configuration")
229+
230+
# Set GTK4 color scheme for GNOME/Cinnamon
231+
if desktop_env in ["GNOME", "Cinnamon"]:
232+
self._run_command([
233+
"dconf", "write",
234+
"/org/gnome/desktop/interface/color-scheme",
235+
"'default'"
236+
])
237+
238+
# Configure Kvantum theme
239+
home = os.path.expanduser("~")
240+
kvantum_dir = os.path.join(home, ".config", "Kvantum")
241+
kvantum_conf = os.path.join(kvantum_dir, "kvantum.kvconfig")
242+
243+
kvantum_content = "[General]\ntheme=BigAdwaitaRoundGtk\n"
244+
self._write_user_config_file(kvantum_conf, kvantum_content)
245+
246+
# Copy kdeglobals for light theme
247+
kdeglobals_source = "/usr/share/sync-kde-and-gtk-places/biglinux"
248+
kdeglobals_dest = os.path.join(home, ".config", "kdeglobals")
249+
if os.path.exists(kdeglobals_source):
250+
self._run_command(["cp", "-f", kdeglobals_source, kdeglobals_dest])
251+
else:
252+
logger.warning(f"Light theme kdeglobals not found at {kdeglobals_source}")
253+
254+
# Apply light icon theme
255+
self._apply_icon_theme_variant(desktop_env, dark=False)
256+
257+
def _apply_icon_theme_variant(self, desktop_env: str, dark: bool):
258+
"""Applies appropriate icon theme variant (dark or light)."""
259+
logger.debug(f"Applying {'dark' if dark else 'light'} icon theme variant")
260+
261+
# Get current icon theme
262+
icon_theme = ""
263+
if desktop_env == "XFCE":
264+
success, icon_theme = self._run_command([
265+
"xfconf-query", "-c", "xsettings",
266+
"-p", "/Net/IconThemeName"
267+
], read_only=True)
268+
if success:
269+
icon_theme = icon_theme.strip()
270+
else:
271+
# GNOME or Cinnamon
272+
if desktop_env == "Cinnamon":
273+
success, icon_theme = self._run_command([
274+
"dconf", "read",
275+
"/org/cinnamon/desktop/interface/icon-theme"
276+
], read_only=True)
277+
else:
278+
success, icon_theme = self._run_command([
279+
"dconf", "read",
280+
"/org/gnome/desktop/interface/icon-theme"
281+
], read_only=True)
282+
283+
if success:
284+
icon_theme = icon_theme.strip("'\"")
285+
286+
if not icon_theme:
287+
logger.warning("Could not detect current icon theme")
288+
return
289+
290+
# Find appropriate variant
291+
home = os.path.expanduser("~")
292+
search_paths = [
293+
"/usr/share/icons",
294+
os.path.join(home, ".local/share/icons")
295+
]
296+
297+
new_theme = icon_theme
298+
if dark:
299+
# Look for dark variant
300+
for base_path in search_paths:
301+
if os.path.exists(base_path):
302+
# Try adding -dark suffix
303+
dark_icon_path = os.path.join(base_path, f"{icon_theme}-dark")
304+
if os.path.isdir(dark_icon_path):
305+
new_theme = f"{icon_theme}-dark"
306+
logger.debug(f"Found dark icon theme: {new_theme}")
307+
break
308+
else:
309+
# Remove -dark suffix if present
310+
new_theme = icon_theme.replace("-dark", "").replace("-Dark", "")
311+
logger.debug(f"Using light icon theme: {new_theme}")
312+
313+
# Apply the icon theme
314+
self._set_icon_theme(desktop_env, new_theme)
315+
316+
def _set_icon_theme(self, desktop_env: str, theme_name: str):
317+
"""Sets the icon theme for the desktop environment."""
318+
logger.info(f"Setting icon theme to: {theme_name}")
319+
320+
if desktop_env == "Cinnamon":
321+
self._run_command([
322+
"dconf", "write",
323+
"/org/cinnamon/desktop/interface/icon-theme",
324+
f"'{theme_name}'"
325+
])
326+
elif desktop_env == "XFCE":
327+
self._run_command([
328+
"xfconf-query", "-c", "xsettings",
329+
"-p", "/Net/IconThemeName",
330+
"-s", theme_name
331+
])
332+
else: # GNOME
333+
self._run_command([
334+
"dconf", "write",
335+
"/org/gnome/desktop/interface/icon-theme",
336+
f"'{theme_name}'"
337+
])
338+
177339
def finalize_setup(self, config: SetupConfig):
178340
"""
179341
Performs final setup steps, including creating flag files.
@@ -306,19 +468,38 @@ def check_jamesdsp_availability(self) -> bool:
306468
return os.path.exists("/usr/bin/jamesdsp")
307469

308470
def check_enhanced_contrast_availability(self) -> bool:
309-
"""Checks for the AppleRGB ICC profile and if kwin_wayland is running."""
471+
"""Checks for the AppleRGB ICC profile and if running on Wayland."""
310472
icc_profile_exists = os.path.exists("/usr/share/color/icc/colord/ECI-RGBv1.icc")
473+
logger.debug(f"ICC profile exists: {icc_profile_exists}")
474+
475+
# Check if running on Wayland (works for GNOME, KDE, etc)
476+
wayland_running = False
477+
478+
# Method 1: Check XDG_SESSION_TYPE environment variable
479+
session_type = os.environ.get("XDG_SESSION_TYPE", "").lower()
480+
logger.debug(f"XDG_SESSION_TYPE: {session_type}")
481+
if session_type == "wayland":
482+
wayland_running = True
483+
484+
# Method 2: Check if WAYLAND_DISPLAY is set
485+
wayland_display = os.environ.get("WAYLAND_DISPLAY", "")
486+
logger.debug(f"WAYLAND_DISPLAY: {wayland_display}")
487+
if not wayland_running and wayland_display:
488+
wayland_running = True
489+
490+
# Method 3: Check for compositor processes (fallback)
491+
if not wayland_running:
492+
try:
493+
# Check for kwin_wayland (KDE) or gnome-shell on Wayland
494+
result = subprocess.run(
495+
["pgrep", "-x", "kwin_wayland"], capture_output=True, check=False
496+
)
497+
wayland_running = result.returncode == 0
498+
except FileNotFoundError:
499+
pass
311500

312-
# Check if kwin_wayland process is running
313-
try:
314-
result = subprocess.run(
315-
["pgrep", "-x", "kwin_wayland"], capture_output=True, check=False
316-
)
317-
kwin_running = result.returncode == 0
318-
except FileNotFoundError:
319-
kwin_running = False # pgrep not found
320-
321-
return icc_profile_exists and kwin_running
501+
logger.info(f"Enhanced contrast availability: ICC={icc_profile_exists}, Wayland={wayland_running}, Result={icc_profile_exists and wayland_running}")
502+
return icc_profile_exists and wayland_running
322503

323504
def get_total_memory_gb(self) -> float:
324505
"""Gets total system memory in Gigabytes from /proc/meminfo."""

‎biglinux-livecd/usr/share/biglinux/livecd/ui/app_window.py‎

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def _build_ui(self):
136136
self.steps = [
137137
{"name": "language", "file": "headerbar-locale.svg"},
138138
{"name": "keyboard", "file": "headerbar-keyboard.svg"},
139+
{"name": "theme", "file": "headerbar-theme.svg"},
139140
]
140141
else:
141142
self.steps = [
@@ -147,9 +148,7 @@ def _build_ui(self):
147148

148149
# Build header layout based on environment
149150
if self.is_simplified_env:
150-
# Simplified layout: [Language] [comm-logo.png] [Keyboard]
151-
self._add_step_button(header_content_box, self.steps[0])
152-
151+
# Simplified layout: [comm-logo.png] [Language] [Keyboard] [Theme]
153152
# Use comm-logo.png for simplified environments (XivaStudio override if exists)
154153
logo_path = get_comm_logo_path(self.system_service)
155154
if os.path.exists(logo_path):
@@ -158,8 +157,10 @@ def _build_ui(self):
158157
logo.set_margin_start(20)
159158
logo.set_margin_end(20)
160159
header_content_box.append(logo)
161-
160+
161+
self._add_step_button(header_content_box, self.steps[0])
162162
self._add_step_button(header_content_box, self.steps[1])
163+
self._add_step_button(header_content_box, self.steps[2])
163164
else:
164165
# Full layout: [Language] [Keyboard] [logo.png] [Desktop] [Theme]
165166
self._add_step_button(header_content_box, self.steps[0])
@@ -219,6 +220,8 @@ def _retranslate_ui(self):
219220
page.set_title(_("Desktop Layout"))
220221
elif view_name == "theme":
221222
page.set_title(_("Theme"))
223+
elif view_name == "simple_theme":
224+
page.set_title(_("Theme"))
222225

223226
# Trigger re-translation within the view itself
224227
if hasattr(view, "_retranslate_ui"):
@@ -233,6 +236,8 @@ def _ensure_view(self, view_name: str, *args):
233236
self._add_desktop_view()
234237
elif view_name == "theme":
235238
self._add_theme_view()
239+
elif view_name == "simple_theme":
240+
self._add_simple_theme_view()
236241

237242
def _add_step_button(self, box, step_info):
238243
button = Gtk.Button()
@@ -267,9 +272,16 @@ def _on_step_button_clicked(self, button, view_name):
267272

268273
def _update_header_state(self):
269274
current_view_name = self.stack.get_visible_child_name()
275+
276+
# Map simple_theme to theme for header state
277+
if current_view_name == "simple_theme":
278+
search_name = "theme"
279+
else:
280+
search_name = current_view_name
281+
270282
try:
271283
current_index = next(
272-
i for i, s in enumerate(self.steps) if s["name"] == current_view_name
284+
i for i, s in enumerate(self.steps) if s["name"] == search_name
273285
)
274286
except StopIteration:
275287
current_index = -1
@@ -372,9 +384,9 @@ def _on_keyboard_selected(self, view, layout):
372384
self.completed_steps.add("keyboard")
373385

374386
if self.is_simplified_env:
375-
# Skip desktop and theme for simplified environments (GNOME/XFCE/Cinnamon)
376-
self.system_service.finalize_setup(self.config)
377-
self.close()
387+
# Navigate to simple theme view for simplified environments (GNOME/XFCE/Cinnamon)
388+
self._ensure_view("simple_theme")
389+
self.stack.set_visible_child_name("simple_theme")
378390
else:
379391
# LAZY LOADING: Ensure desktop view exists before showing it
380392
self._ensure_view("desktop")
@@ -424,6 +436,35 @@ def _on_theme_selected(self, view, theme):
424436
self.system_service.finalize_setup(self.config)
425437
self.close()
426438

439+
def _add_simple_theme_view(self):
440+
"""Creates and adds the simplified theme view for GNOME/XFCE/Cinnamon."""
441+
view = ThemeView(system_service=self.system_service, simplified_mode=True)
442+
view.connect("theme-selected", self._on_simple_theme_selected)
443+
self.stack.add_titled(view, "simple_theme", _("Theme"))
444+
445+
def _on_simple_theme_selected(self, view, theme):
446+
"""Handle theme selection from simplified theme view."""
447+
logger.info(f"Simple theme selected: {theme}")
448+
449+
# Mark theme step as completed
450+
self.completed_steps.add("theme")
451+
452+
# Save to config
453+
self.config.simple_theme = theme
454+
455+
# Apply simple theme (light or dark)
456+
self.system_service.apply_simple_theme(theme)
457+
458+
# Get JamesDSP and contrast settings from the theme view
459+
theme_view = self.stack.get_child_by_name("simple_theme")
460+
if theme_view:
461+
self.config.enable_jamesdsp = theme_view.is_jamesdsp_enabled()
462+
self.config.enable_enhanced_contrast = theme_view.is_contrast_enabled()
463+
464+
# Finalize and close
465+
self.system_service.finalize_setup(self.config)
466+
self.close()
467+
427468
def _on_key_press_event(self, controller, keyval, keycode, state):
428469
current_view = self.stack.get_visible_child()
429470
if isinstance(current_view, LanguageView):

0 commit comments

Comments
 (0)