01-03-2026, 03:02 PM
01-03-2026, 05:23 PM
Wow, that was fast! Thanks Jerry!
Could you supply the file name and line # with the code change and I'll test again (in dark mode etc.)
Cheers!
Could you supply the file name and line # with the code change and I'll test again (in dark mode etc.)
Cheers!
01-04-2026, 07:25 AM
Just replace the existing code with this:
Code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Lite Welcome - GTK4 Native Version
# Copyright © 2012-2013 "Korora Project" <[email protected]>
# Copyright © 2013-2015 "Manjaro Linux" <[email protected]>
# Copyright © 2014-2025 "Jerry Bezencon" <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import signal
import subprocess
from webbrowser import open_new_tab
# Force CPU/Cairo rendering for VM compatibility (fixes white window issues)
os.environ['GSK_RENDERER'] = 'cairo'
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Gdk, GLib, Gio, Adw, GdkPixbuf
# Try to import GdkX11 for window centering on X11 systems
try:
gi.require_version('GdkX11', '4.0')
from gi.repository import GdkX11
HAS_X11 = True
except (ValueError, ImportError):
HAS_X11 = False
# Application constants
APP_ID = "com.linuxlite.welcome"
APP_NAME = "Lite Welcome"
ICON_NAME = "litewelcome"
# Detect Live mode by checking for ubiquity installer on desktop
IS_LIVE_MODE = os.path.exists(os.path.expanduser("~/Desktop/ubiquity.desktop"))
def load_image_from_file(filepath, width=None, height=None):
"""
Load an image from file using GdkPixbuf (CPU-based, reliable).
"""
if not os.path.exists(filepath):
print(f"File not found: {filepath}")
return None
try:
# Use GdkPixbuf for reliable CPU-based rendering
if width and height:
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(filepath, width, height, True)
else:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
# Create Gtk.Image directly from pixbuf for GTK4
texture = Gdk.Texture.new_for_pixbuf(pixbuf)
image = Gtk.Image.new_from_paintable(texture)
return image
except Exception as e:
print(f"Error loading image {filepath}: {e}")
return None
class WelcomeConfig:
def __init__(self):
self._arch = '64-bit' if os.uname()[4] == 'x86_64' else '32-bit'
self._config_dir = os.path.expanduser('~/.config/lite/welcome/')
self._autostart_path = os.path.expanduser('~/.config/autostart/lite-welcome.desktop')
os.makedirs(self._config_dir, exist_ok=True)
self._autostart = os.path.exists(self._autostart_path)
@property
def autostart(self):
return self._autostart
def toggle_autostart(self):
try:
if not self._autostart:
os.makedirs(os.path.dirname(self._autostart_path), exist_ok=True)
os.symlink('/usr/share/applications/lite-welcome.desktop', self._autostart_path)
else:
if os.path.exists(self._autostart_path):
os.unlink(self._autostart_path)
except OSError as e:
print(f"Error toggling autostart: {e}")
self._autostart = os.path.exists(self._autostart_path)
return self._autostart
class CommandHandler:
def __init__(self, app=None):
self.app = app
@staticmethod
def spawn(args):
try:
subprocess.Popen(args, start_new_session=True)
except Exception as e:
print(f"Error spawning process: {e}")
@staticmethod
def open_url(url):
try:
open_new_tab(url)
except Exception as e:
print(f"Error opening URL: {e}")
def show_feedback(self, message):
"""Show toast feedback if app reference is available."""
if self.app and hasattr(self.app, 'show_toast'):
self.app.show_toast(message)
def execute(self, command):
if command == 'start_updates':
if os.path.exists("/usr/bin/lite-updates"):
self.show_feedback("Launching Install Updates...")
self.spawn(['pkexec', '/usr/bin/lite-updates'])
elif command == 'install_drivers':
if os.path.exists("/usr/bin/software-properties-gtk"):
self.show_feedback("Launching Driver Manager...")
self.spawn(['/usr/bin/software-properties-gtk', '--open-tab=4'])
elif command == 'timeshift':
if os.path.exists("/usr/bin/timeshift-launcher"):
self.show_feedback("Launching Timeshift...")
self.spawn(['/usr/bin/timeshift-launcher'])
elif command == 'start_software':
if os.path.exists("/usr/bin/lite-software"):
self.show_feedback("Launching Install Software...")
self.spawn(['pkexec', 'lite-software'])
elif command == 'upgrade':
if os.path.exists("/usr/bin/lite-upgrade-series7"):
self.show_feedback("Launching Upgrade Tool...")
self.spawn(['/usr/bin/lite-upgrade-series7'])
elif command == 'lite-manual':
self.show_feedback("Opening Help Manual...")
self.open_url("https://wiki.linuxliteos.com/en/home")
elif command == 'installlang':
self.show_feedback("Opening Language Settings...")
self.open_url("https://wiki.linuxliteos.com/en/install.html#setlang")
self.spawn(['/usr/bin/gnome-language-selector'])
elif command == 'lighttheme':
self.show_feedback("Applying Light Theme...")
self.spawn(["xfconf-query", "-c", "xsettings", "-p", "/Net/ThemeName", "-s", "Materia"])
self.spawn(["xfconf-query", "-c", "xsettings", "-p", "/Net/IconThemeName", "-s", "Papirus-Adapta"])
self.spawn(["zenity", "--title=Lite Welcome", "--info", "--width=320", "--text=Light Theme applied"])
elif command == 'darktheme':
self.show_feedback("Applying Dark Theme...")
self.spawn(["xfconf-query", "-c", "xsettings", "-p", "/Net/ThemeName", "-s", "Materia-dark"])
self.spawn(["xfconf-query", "-c", "xsettings", "-p", "/Net/IconThemeName", "-s", "Papirus-Adapta-Nokto"])
self.spawn(["zenity", "--title=Lite Welcome", "--info", "--width=320", "--text=Dark Theme applied"])
elif command == 'install-now':
self.spawn(["sudo", "--preserve-env=DBUS_SESSION_BUS_ADDRESS,XDG_RUNTIME_DIR", "pkexec", "ubiquity", "gtk_ui"])
self.spawn(["zenity", "--timeout", "5", "--title=Lite Welcome", "--info", "--width=200", "--text=Loading please wait..."])
self.spawn(["killall", "lite-welcome"])
elif command.startswith("link:"):
self.show_feedback("Opening in browser...")
self.open_url(command[5:])
class NavigationPage(Gtk.Box):
def __init__(self, app, title=""):
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=0)
self.app = app
self.title = title
self.cmd_handler = app.cmd_handler # Use app's command handler for toast support
def create_header(self, title):
header_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
header_box.set_margin_top(16)
header_box.set_margin_bottom(16)
title_label = Gtk.Label(label=title)
title_label.add_css_class("title-1")
title_label.set_halign(Gtk.Align.CENTER)
header_box.append(title_label)
return header_box
def create_button(self, label, icon_name, command=None, style_class="suggested-action"):
button = Gtk.Button()
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
box.set_halign(Gtk.Align.CENTER)
if icon_name:
icon = Gtk.Image.new_from_icon_name(icon_name)
box.append(icon)
lbl = Gtk.Label(label=label)
box.append(lbl)
button.set_child(box)
if style_class:
button.add_css_class(style_class)
if command:
button.connect("clicked", lambda b: self.cmd_handler.execute(command))
return button
class HomePage(NavigationPage):
def __init__(self, app, data_path):
super().__init__(app, "Home")
self.data_path = data_path
self.build_ui()
def build_ui(self):
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scrolled.set_vexpand(True)
content = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16)
content.set_margin_top(16)
content.set_margin_bottom(16)
content.set_margin_start(24)
content.set_margin_end(24)
# Logo - use PNG for maximum compatibility (SVG has rendering issues in some VMs)
logo_path = os.path.join(self.data_path, "img", "lite-welcome.png")
if os.path.exists(logo_path):
# Use Gtk.Picture for proper image display at desired size
logo = Gtk.Picture.new_for_filename(logo_path)
logo.set_content_fit(Gtk.ContentFit.CONTAIN)
logo.set_can_shrink(True)
logo.set_size_request(291, 160) # Explicit minimum size
logo_button = Gtk.Button()
logo_button.set_child(logo)
logo_button.add_css_class("flat")
logo_button.set_halign(Gtk.Align.CENTER)
logo_button.connect("clicked", lambda b: self.cmd_handler.open_url("https://mityer.khdlhfjijfrupr.tk/"))
content.append(logo_button)
# Summary
summary_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
summary_box.set_margin_top(16)
summary_box.set_margin_bottom(16)
summary1 = Gtk.Label()
summary1.set_wrap(True)
summary1.set_justify(Gtk.Justification.CENTER)
summary1.set_markup("Linux Lite is free for everyone to use and share, and suitable for people who are new to Linux or for people who want a lightweight environment that is also fully functional.")
summary_box.append(summary1)
summary2 = Gtk.Label()
summary2.set_wrap(True)
summary2.set_justify(Gtk.Justification.CENTER)
summary2.set_markup("Linux Lite provides a basic collection of everyday tools: a web browser, an email client, a media player, an office suite, an image editor, and so on.")
summary_box.append(summary2)
content.append(summary_box)
# Columns
columns_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=24)
columns_box.set_halign(Gtk.Align.CENTER)
columns_box.set_homogeneous(True)
start_column = self.create_button_column("Start Here", [
("Install Updates", "software-update-available-symbolic", "page:start#updates"),
("Install Drivers", "preferences-system-symbolic", "page:start#drivers"),
("Set a Restore Point", "computer-symbolic", "page:start#restore"),
("Install Language Support", "preferences-desktop-locale-symbolic", "page:start#language"),
("Select Dark or Light Theme", "preferences-desktop-display-symbolic", "page:start#theme"),
], "suggested-action")
columns_box.append(start_column)
support_column = self.create_button_column("Support", [
("Online Support", "help-about-symbolic", "page:support#online"),
("Linux Lite Wiki", "accessories-dictionary-symbolic", "page:support#wiki"),
("Forums", "system-users-symbolic", "page:support#forums"),
("Hardware Database", "printer-symbolic", "page:support#hardware"),
("UEFI & Secure Boot", "security-high-symbolic", "page:start#uefi"),
], "")
if IS_LIVE_MODE:
install_btn = Gtk.Button()
install_btn.set_size_request(-1, 48)
install_btn.set_margin_top(8)
install_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
install_box.set_halign(Gtk.Align.CENTER)
install_icon = Gtk.Image.new_from_icon_name("system-software-install-symbolic")
install_box.append(install_icon)
install_lbl = Gtk.Label(label="INSTALL NOW")
install_box.append(install_lbl)
install_btn.set_child(install_box)
install_btn.add_css_class("destructive-action")
install_btn.connect("clicked", lambda b: self.cmd_handler.execute("install-now"))
support_column.append(install_btn)
columns_box.append(support_column)
contribute_column = self.create_button_column("Contribute", [
("Code", "utilities-terminal-symbolic", "page:contribute#code"),
("Donate", "emblem-favorite-symbolic", "page:contribute#donate"),
("Host a Mirror", "folder-download-symbolic", "page:contribute#mirror"),
("Social Media", "emblem-shared-symbolic", "page:contribute#social"),
("Feedback", "document-edit-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/feedback.html"),
], "")
columns_box.append(contribute_column)
content.append(columns_box)
scrolled.set_child(content)
self.append(scrolled)
def create_button_column(self, title, buttons, style_class):
column = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
column.set_size_request(200, -1)
title_label = Gtk.Label(label=title)
title_label.add_css_class("heading")
title_label.set_margin_bottom(8)
column.append(title_label)
for label, icon, cmd in buttons:
button = Gtk.Button()
button.set_size_request(-1, 40)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
box.set_halign(Gtk.Align.CENTER)
if icon:
icon_widget = Gtk.Image.new_from_icon_name(icon)
box.append(icon_widget)
lbl = Gtk.Label(label=label)
box.append(lbl)
button.set_child(box)
if style_class:
button.add_css_class(style_class)
if cmd.startswith("page:"):
page_cmd = cmd[5:]
button.connect("clicked", lambda b, p=page_cmd: self.app.navigate_to(p))
elif cmd.startswith("link:"):
url = cmd[5:]
button.connect("clicked", lambda b, u=url: self.cmd_handler.open_url(u))
column.append(button)
return column
class StartPage(NavigationPage):
def __init__(self, app, data_path):
super().__init__(app, "Starting with Linux Lite")
self.data_path = data_path
self.sections = {} # Store section references for scrolling
self.scrolled = None
self.build_ui()
def build_ui(self):
self.scrolled = Gtk.ScrolledWindow()
self.scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self.scrolled.set_vexpand(True)
content = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=24)
content.set_margin_top(24)
content.set_margin_bottom(24)
content.set_margin_start(32)
content.set_margin_end(32)
header = self.create_header("Starting with Linux Lite")
content.append(header)
# Intro text
intro = Gtk.Label()
intro.set_markup("Once you have installed Linux Lite and before you can begin using it, it is strongly recommended that you first complete the 4 steps listed below.\n\n<b>If you are running the Live version of Linux Lite, do not attempt these steps below.</b>\n\nIf you have just finished installing Linux Lite in a language other than English, please restart your computer after these steps to activate all supported Menu translations.")
intro.set_wrap(True)
intro.set_justify(Gtk.Justification.CENTER)
content.append(intro)
# Step 1: Install Updates
step1_desc = "First you need to update your system. Click on the button below to Install Updates now.\n\nOn the window that pops up, enter the password of the user you created during the installation.\n\nYou can also Install Updates via the menu. Click on Menu, Favorites, Install Updates."
self.sections["updates"] = self.create_step_section("✓ Step 1: Install Updates", step1_desc, "Install Updates", "software-update-available-symbolic", "start_updates")
content.append(self.sections["updates"])
# Step 2: Install Drivers
step2_desc = "Now, let's see if you need any drivers installed. Click on the button below to check.\n\nYou can also Install Drivers via the menu. Click on Menu, Settings, Install Drivers."
self.sections["drivers"] = self.create_step_section("✓ Step 2: Install Drivers", step2_desc, "Install Drivers", "preferences-system-symbolic", "install_drivers")
content.append(self.sections["drivers"])
# Step 3: Restore Point
step3_desc = "Last step is to create, just like on Windows, a restore point that you can restore from in case something goes wrong. When you are ready, click on the button below.\n\nYou can also access Timeshift via the menu. Click on Menu, System, Timeshift."
self.sections["restore"] = self.create_step_section("✓ Step 3: Setting a Restore Point", step3_desc, "Set a Restore Point", "computer-symbolic", "timeshift")
content.append(self.sections["restore"])
# Step 4: Language Support
step4_desc = "Click on the button below to install Language Support for Linux Lite.\n\nYou can also install Language Support via the menu. Click on Menu, Settings, Language Support.\n\n<b>NOTE: Don't forget to restart your computer after you have finished installing additional Language Support.</b>"
self.sections["language"] = self.create_step_section("✓ Step 4: Installing Language Support", step4_desc, "Install Language Support", "preferences-desktop-locale-symbolic", "installlang")
content.append(self.sections["language"])
# UEFI and Secure Boot section
self.sections["uefi"] = self.create_uefi_section()
content.append(self.sections["uefi"])
# Theme section
self.sections["theme"] = self.create_theme_section()
content.append(self.sections["theme"])
# Keyboard and Numlock section
self.sections["keyboard"] = self.create_keyboard_section()
content.append(self.sections["keyboard"])
# Upgrading section
self.sections["upgrade"] = self.create_upgrade_section()
content.append(self.sections["upgrade"])
# Lite Software section
self.sections["software"] = self.create_software_section()
content.append(self.sections["software"])
# Hardware Recommendations section
self.sections["hardware"] = self.create_hardware_section()
content.append(self.sections["hardware"])
self.scrolled.set_child(content)
self.append(self.scrolled)
def scroll_to_section(self, section_name):
if section_name not in self.sections:
return
self.pending_scroll = section_name
GLib.idle_add(self._perform_scroll)
def _perform_scroll(self):
if not hasattr(self, 'pending_scroll') or not self.pending_scroll:
return False
section_name = self.pending_scroll
self.pending_scroll = None
if section_name not in self.sections:
return False
target = self.sections[section_name]
content = self.scrolled.get_child()
if hasattr(content, 'get_child'):
content = content.get_child()
y_pos = 0
child = content.get_first_child() if content else None
while child:
if child == target:
break
h = child.get_height()
if h <= 0:
h = child.get_preferred_size().minimum_size.height
y_pos += h + 24
child = child.get_next_sibling()
adj = self.scrolled.get_vadjustment()
adj.set_value(y_pos)
return False
def create_step_section(self, title, description, button_label, icon_name, command):
frame = Gtk.Frame()
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
box.add_css_class("section")
box.set_margin_top(16)
box.set_margin_bottom(16)
box.set_margin_start(16)
box.set_margin_end(16)
title_label = Gtk.Label()
title_label.set_markup(f"<span size='large' weight='bold'>{title}</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
desc_label = Gtk.Label()
desc_label.set_markup(description)
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
button = self.create_button(button_label, icon_name, command, "suggested-action")
button.set_halign(Gtk.Align.CENTER)
box.append(button)
frame.set_child(box)
return frame
def create_uefi_section(self):
frame = Gtk.Frame()
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.add_css_class("section")
box.set_margin_top(16)
box.set_margin_bottom(16)
box.set_margin_start(16)
box.set_margin_end(16)
title_label = Gtk.Label()
title_label.set_markup("<span size='large' weight='bold'>? UEFI and Secure Boot</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
# UEFI image
uefi_img_path = os.path.join(self.data_path, "img", "uefisb.png")
if os.path.exists(uefi_img_path):
uefi_img = load_image_from_file(uefi_img_path, width=500, height=300)
if uefi_img:
uefi_img.set_halign(Gtk.Align.CENTER)
uefi_img.set_valign(Gtk.Align.CENTER)
uefi_img.set_size_request(500, 300)
box.append(uefi_img)
uefi_text = """<b>How do I know if my computer has UEFI?</b>
In Windows Search, type <b>msinfo</b> or <b>msinfo32</b> and launch the desktop app named System Information. Look for the BIOS Mode item, and if the value for it is <b>UEFI</b>, then you have the UEFI firmware. If it says BIOS Mode Legacy, then that's the firmware you're running.
If you bought the computer/motherboard after 2010, chances are you have a UEFI system. If you are still unsure, download the UEFI version as it will also detect and run on a BIOS-Legacy computer.
<b>Secure Boot</b>
Linux Lite recommends that you disable Secure Boot in your BIOS. This will save you potentially a lot of headaches during the use of your system. Linux Lite will run with Secure Boot enabled, but we highly recommend that you don't."""
desc_label = Gtk.Label()
desc_label.set_markup(uefi_text)
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
frame.set_child(box)
return frame
def create_theme_section(self):
frame = Gtk.Frame()
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
box.add_css_class("section")
box.set_margin_top(16)
box.set_margin_bottom(16)
box.set_margin_start(16)
box.set_margin_end(16)
title_label = Gtk.Label()
title_label.set_markup("<span size='large' weight='bold'>? Select a Light or Dark Theme</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
desc_label = Gtk.Label(label="Click on a button below to select either a Light Theme or a Dark Theme. The Light Theme is already the default theme.")
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
# Light theme with preview
light_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
light_box.add_css_class("section")
light_box.set_halign(Gtk.Align.CENTER)
light_btn = self.create_button("Light Theme", "weather-clear-symbolic", "lighttheme", "suggested-action")
light_box.append(light_btn)
light_img_path = os.path.join(self.data_path, "img", "lightth.png")
if os.path.exists(light_img_path):
light_img = load_image_from_file(light_img_path, width=380, height=192)
if light_img:
light_img.set_halign(Gtk.Align.CENTER)
light_img.set_valign(Gtk.Align.CENTER)
light_img.set_size_request(380, 192)
light_box.append(light_img)
box.append(light_box)
# Dark theme with preview
dark_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
dark_box.add_css_class("section")
dark_box.set_halign(Gtk.Align.CENTER)
dark_btn = self.create_button("Dark Theme", "weather-clear-night-symbolic", "darktheme", "")
dark_box.append(dark_btn)
dark_img_path = os.path.join(self.data_path, "img", "darkth.png")
if os.path.exists(dark_img_path):
dark_img = load_image_from_file(dark_img_path, width=380, height=192)
if dark_img:
dark_img.set_halign(Gtk.Align.CENTER)
dark_img.set_valign(Gtk.Align.CENTER)
dark_img.set_size_request(380, 192)
dark_box.append(dark_img)
box.append(dark_box)
frame.set_child(box)
return frame
def create_keyboard_section(self):
frame = Gtk.Frame()
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
box.add_css_class("section")
box.set_margin_top(16)
box.set_margin_bottom(16)
box.set_margin_start(16)
box.set_margin_end(16)
title_label = Gtk.Label()
title_label.set_markup("<span size='large' weight='bold'>⌨️ Keyboard and Numlock</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
kb_text = """There are thousands of computing configurations in existence. As a result, different manufacturers have different ways of implementing their Keyboard and Numlock settings. When you boot Linux Lite for the first time and are having issues with your Keyboard and or Numlock, try each of the following solutions:
• Check your BIOS/UEFI configuration
• Menu, Settings, Lite Tweaks, Numlock
• FN (Function) + NUM LOCK
• FN + F11 (Acer, Toshiba, Samsung)
• Shift + NUM LOCK
• FN + NUM LOCK (Sony, Gateway, Lenovo, ASUS)
• FN + F8 (HP)
• Ctrl + F11
• FN + Shift + NUM LOCK
• FN + F4 (Dell)
<b>Keep Numlock working between boots</b> - Menu, Settings, Keyboard, Behavior tab > Enable or Disable - <b>Restore num lock state on startup</b>"""
desc_label = Gtk.Label()
desc_label.set_markup(kb_text)
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
frame.set_child(box)
return frame
def create_upgrade_section(self):
frame = Gtk.Frame()
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
box.add_css_class("section")
box.set_margin_top(16)
box.set_margin_bottom(16)
box.set_margin_start(16)
box.set_margin_end(16)
title_label = Gtk.Label()
title_label.set_markup("<span size='large' weight='bold'>? Upgrading</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
upgrade_text = """Each <b>Series</b> of Linux Lite lasts 2 years and is based off LTS (Long Term Support) which continues to provide updates for 5 years. eg. Linux Lite <b>4.0</b> - Linux Lite <b>4.8</b> is <b>Series 4</b>, Linux Lite <b>5.0</b> is the start of <b>Series 5</b>, and so on.
Upgrading within <b>Series 7</b> is simple. Click on <b>Menu, Settings, Lite Upgrade</b> and follow the prompts to get the latest version of Linux Lite.
Upgrading can only occur from within a <b>Series</b>. For example we will upgrade you from Linux Lite <b>7.0</b> to Linux Lite <b>7.8</b>, but not from Linux Lite <b>6.0</b> to Linux Lite <b>7.8</b>."""
desc_label = Gtk.Label()
desc_label.set_markup(upgrade_text)
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
button = self.create_button("Upgrade to 7.8", "system-upgrade-symbolic", "upgrade", "suggested-action")
button.set_halign(Gtk.Align.CENTER)
box.append(button)
frame.set_child(box)
return frame
def create_software_section(self):
frame = Gtk.Frame()
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
box.add_css_class("section")
box.set_margin_top(16)
box.set_margin_bottom(16)
box.set_margin_start(16)
box.set_margin_end(16)
title_label = Gtk.Label()
title_label.set_markup("<span size='large' weight='bold'>? Lite Software</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
software_text = """We've made it as simple as just a few clicks to install many of your favorite programs.
On Linux Lite you can install:
• Dropbox
• Firefox Web Browser
• Kodi
• Spotify
• Steam
• Teamviewer
• Tor Web Browser
• VirtualBox
• Zoom
Click on <b>Menu, Settings, Lite Software</b> and follow the onscreen prompts to install popular software or click on the button below."""
desc_label = Gtk.Label()
desc_label.set_markup(software_text)
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
button = self.create_button("Lite Software", "system-software-install-symbolic", "start_software", "")
button.set_halign(Gtk.Align.CENTER)
box.append(button)
frame.set_child(box)
return frame
def create_hardware_section(self):
frame = Gtk.Frame()
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
box.add_css_class("section")
box.set_margin_top(16)
box.set_margin_bottom(16)
box.set_margin_start(16)
box.set_margin_end(16)
title_label = Gtk.Label()
title_label.set_markup("<span size='large' weight='bold'>? Hardware Recommendations</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
hw_text = """Linux Lite can run on a wide range of hardware. Our online Hardware Database contains a growing list of computers that can run Linux Lite.
<b>Recommended Computer Requirements:</b>
• 1.5 Ghz Dual Core Processor
• 4 GB Memory
• 40 GB HDD/SSD/NVME
• VGA, DVI, DP or HDMI screen capable of 1366x768 resolution
• DVD drive or USB port for the ISO image
• Disable Secure Boot
<b>TIP:</b> Check out the Linux Lite Hardware Database for a list of over 97,000 computers that can run Linux Lite."""
desc_label = Gtk.Label()
desc_label.set_markup(hw_text)
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
button = self.create_button("Hardware Database", "computer-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/hardware.php", "")
button.set_halign(Gtk.Align.CENTER)
box.append(button)
frame.set_child(box)
return frame
class SupportPage(NavigationPage):
def __init__(self, app, data_path):
super().__init__(app, "Get Support")
self.data_path = data_path
self.sections = {}
self.scrolled = None
self.build_ui()
def build_ui(self):
self.scrolled = Gtk.ScrolledWindow()
self.scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self.scrolled.set_vexpand(True)
content = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=24)
content.set_margin_top(24)
content.set_margin_bottom(24)
content.set_margin_start(32)
content.set_margin_end(32)
header = self.create_header("Get Support")
content.append(header)
# Online Support intro
intro_frame = Gtk.Frame()
intro_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
intro_box.add_css_class("section")
intro_box.set_margin_top(16)
intro_box.set_margin_bottom(16)
intro_box.set_margin_start(16)
intro_box.set_margin_end(16)
intro_title = Gtk.Label()
intro_title.set_markup("<span size='large' weight='bold'>? Online Support</span>")
intro_title.set_halign(Gtk.Align.CENTER)
intro_box.append(intro_title)
intro_desc = Gtk.Label(label="Linux Lite aims to provide you with a variety of Support options.")
intro_desc.set_wrap(True)
intro_desc.set_halign(Gtk.Align.CENTER)
intro_desc.set_justify(Gtk.Justification.CENTER)
intro_box.append(intro_desc)
intro_frame.set_child(intro_box)
self.sections["online"] = intro_frame
content.append(intro_frame)
# Linux Lite Wiki section
wiki_frame = Gtk.Frame()
wiki_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
wiki_box.add_css_class("section")
wiki_box.set_margin_top(16)
wiki_box.set_margin_bottom(16)
wiki_box.set_margin_start(16)
wiki_box.set_margin_end(16)
wiki_title = Gtk.Label()
wiki_title.set_markup("<span size='large' weight='bold'>? Linux Lite Wiki</span>")
wiki_title.set_halign(Gtk.Align.CENTER)
wiki_box.append(wiki_title)
wiki_desc = Gtk.Label()
wiki_desc.set_markup("Click on <b>Menu, Favorites, Linux Lite Wiki</b>. The Wiki is divided into clear categories - <b>Install Guide, Network, Software and Hardware</b>. Each tutorial has step by step instructions, with accompanying pictures. You can also access our Wiki online by clicking below:")
wiki_desc.set_wrap(True)
wiki_desc.set_halign(Gtk.Align.CENTER)
wiki_desc.set_justify(Gtk.Justification.CENTER)
wiki_box.append(wiki_desc)
wiki_btn = self.create_button("Online Wiki", "accessories-dictionary-symbolic", "link:https://wiki.linuxliteos.com/en/home", "")
wiki_btn.set_halign(Gtk.Align.CENTER)
wiki_box.append(wiki_btn)
wiki_frame.set_child(wiki_box)
self.sections["wiki"] = wiki_frame
content.append(wiki_frame)
# Forums section
forums_frame = Gtk.Frame()
forums_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
forums_box.add_css_class("section")
forums_box.set_margin_top(16)
forums_box.set_margin_bottom(16)
forums_box.set_margin_start(16)
forums_box.set_margin_end(16)
forums_title = Gtk.Label()
forums_title.set_markup("<span size='large' weight='bold'>? Forums</span>")
forums_title.set_halign(Gtk.Align.CENTER)
forums_box.append(forums_title)
forums_desc = Gtk.Label()
forums_desc.set_markup("Forums are a great resource for information. Begin by searching for your problem:\n\nIf no results turn up, by all means please post a new thread in the correct section clearly describing your situation. Once you have activated your account, you'll need to login before you can post.")
forums_desc.set_wrap(True)
forums_desc.set_halign(Gtk.Align.CENTER)
forums_desc.set_justify(Gtk.Justification.CENTER)
forums_box.append(forums_desc)
forums_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
forums_buttons.set_halign(Gtk.Align.CENTER)
search_btn = self.create_button("Search Forums", "system-search-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/forums/search.php", "")
forums_buttons.append(search_btn)
register_btn = self.create_button("Register", "contact-new-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/forums/member.php?action=register", "")
forums_buttons.append(register_btn)
login_btn = self.create_button("Login", "system-users-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/forums/", "")
forums_buttons.append(login_btn)
forums_box.append(forums_buttons)
forums_frame.set_child(forums_box)
self.sections["forums"] = forums_frame
content.append(forums_frame)
# Hardware Database section
hw_frame = Gtk.Frame()
hw_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
hw_box.add_css_class("section")
hw_box.set_margin_top(16)
hw_box.set_margin_bottom(16)
hw_box.set_margin_start(16)
hw_box.set_margin_end(16)
hw_title = Gtk.Label()
hw_title.set_markup("<span size='large' weight='bold'>? Hardware Database</span>")
hw_title.set_halign(Gtk.Align.CENTER)
hw_box.append(hw_title)
hw_desc = Gtk.Label()
hw_desc.set_markup("The purpose of the <b>Linux Lite Hardware Database</b> is to give people an idea of different computer configurations from within a Linux Lite Series including, <b>Make and Model, CPU, Graphics, Audio, Network and Storage</b> technical specifications.\n\nThese hardware combinations provide a snapshot of the kind of computers people are able to use with Linux Lite. This makes it a great resource for people either thinking of buying new hardware, or seeing if their existing machine is capable of running Linux Lite.")
hw_desc.set_wrap(True)
hw_desc.set_halign(Gtk.Align.CENTER)
hw_desc.set_justify(Gtk.Justification.CENTER)
hw_box.append(hw_desc)
hw_btn = self.create_button("Hardware Database", "computer-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/hardware.php", "")
hw_btn.set_halign(Gtk.Align.CENTER)
hw_box.append(hw_btn)
hw_frame.set_child(hw_box)
self.sections["hardware"] = hw_frame
content.append(hw_frame)
# Tip box
tip_frame = Gtk.Frame()
tip_frame.add_css_class("success")
tip_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
tip_box.set_margin_top(12)
tip_box.set_margin_bottom(12)
tip_box.set_margin_start(12)
tip_box.set_margin_end(12)
tip_label = Gtk.Label()
tip_label.set_markup("<b>TIP:</b> The Forums are the best avenue for seeking answers. Our members are very generous with their time and there are always people willing to help you. Get help today.")
tip_label.set_wrap(True)
tip_label.set_halign(Gtk.Align.CENTER)
tip_label.set_justify(Gtk.Justification.CENTER)
tip_box.append(tip_label)
tip_frame.set_child(tip_box)
content.append(tip_frame)
self.scrolled.set_child(content)
self.append(self.scrolled)
def scroll_to_section(self, section_name):
if section_name not in self.sections:
return
self.pending_scroll = section_name
GLib.idle_add(self._perform_scroll)
def _perform_scroll(self):
if not hasattr(self, 'pending_scroll') or not self.pending_scroll:
return False
section_name = self.pending_scroll
self.pending_scroll = None
if section_name not in self.sections:
return False
target = self.sections[section_name]
content = self.scrolled.get_child()
if hasattr(content, 'get_child'):
content = content.get_child()
y_pos = 0
child = content.get_first_child() if content else None
while child:
if child == target:
break
h = child.get_height()
if h <= 0:
h = child.get_preferred_size().minimum_size.height
y_pos += h + 24
child = child.get_next_sibling()
adj = self.scrolled.get_vadjustment()
adj.set_value(y_pos)
return False
class ContributePage(NavigationPage):
def __init__(self, app, data_path):
super().__init__(app, "Contribute")
self.data_path = data_path
self.sections = {}
self.scrolled = None
self.build_ui()
def build_ui(self):
self.scrolled = Gtk.ScrolledWindow()
self.scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self.scrolled.set_vexpand(True)
content = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=24)
content.set_margin_top(24)
content.set_margin_bottom(24)
content.set_margin_start(32)
content.set_margin_end(32)
header = self.create_header("Contribute")
content.append(header)
# Code section
code_frame = Gtk.Frame()
code_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
code_box.add_css_class("section")
code_box.set_margin_top(16)
code_box.set_margin_bottom(16)
code_box.set_margin_start(16)
code_box.set_margin_end(16)
code_title = Gtk.Label()
code_title.set_markup("<span size='large' weight='bold'>? Code</span>")
code_title.set_halign(Gtk.Align.CENTER)
code_box.append(code_title)
code_desc = Gtk.Label()
code_desc.set_markup("This is a very exciting time in Linux Lite development. We are in the process of producing custom, free software for Linux Lite. Our philosophy is basic code, simple design and minimal dependency. Our applications should be clean, fast and simple. Intelligent, well thought out design with great attention to detail.\n\nWe use the GPL v2. If you're a developer and would like to help with Linux Lite, click below. We also pay developers to help improve our software.")
code_desc.set_wrap(True)
code_desc.set_halign(Gtk.Align.CENTER)
code_desc.set_justify(Gtk.Justification.CENTER)
code_box.append(code_desc)
code_btn = self.create_button("Learn More", "utilities-terminal-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/contribute.html", "")
code_btn.set_halign(Gtk.Align.CENTER)
code_box.append(code_btn)
code_frame.set_child(code_box)
self.sections["code"] = code_frame
content.append(code_frame)
# Donate section
donate_frame = Gtk.Frame()
donate_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
donate_box.add_css_class("section")
donate_box.set_margin_top(16)
donate_box.set_margin_bottom(16)
donate_box.set_margin_start(16)
donate_box.set_margin_end(16)
donate_title = Gtk.Label()
donate_title.set_markup("<span size='large' weight='bold'>❤️ Donate</span>")
donate_title.set_halign(Gtk.Align.CENTER)
donate_box.append(donate_title)
donate_desc = Gtk.Label()
donate_desc.set_markup("Linux Lite is free and can best be described as a labour of love.\n\nThe goal of this project is to work on it full time so that we can deliver to you a better operating system with each release. More time, more features, greater options. If you use Linux Lite and want to contribute towards its success, consider donating.\n\nDonations go towards development and online services such as websites and repositories, as well as hardware purchases. Every donor is listed on our donate page, regardless of their contribution. Thanks for making a difference and for supporting free software.\n\nLinux Lite pays developers to help improve our custom software through developer services such as UpWork.")
donate_desc.set_wrap(True)
donate_desc.set_halign(Gtk.Align.CENTER)
donate_desc.set_justify(Gtk.Justification.CENTER)
donate_box.append(donate_desc)
donate_btn = self.create_button("Donate", "emblem-favorite-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/donate.html", "suggested-action")
donate_btn.set_halign(Gtk.Align.CENTER)
donate_box.append(donate_btn)
donate_frame.set_child(donate_box)
self.sections["donate"] = donate_frame
content.append(donate_frame)
# Host a Mirror section
mirror_frame = Gtk.Frame()
mirror_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
mirror_box.add_css_class("section")
mirror_box.set_margin_top(16)
mirror_box.set_margin_bottom(16)
mirror_box.set_margin_start(16)
mirror_box.set_margin_end(16)
mirror_title = Gtk.Label()
mirror_title.set_markup("<span size='large' weight='bold'>? Host a Mirror</span>")
mirror_title.set_halign(Gtk.Align.CENTER)
mirror_box.append(mirror_title)
mirror_desc = Gtk.Label()
mirror_desc.set_markup("Help support the Linux Lite community by hosting an official mirror. By providing a mirror, you'll help users worldwide enjoy faster downloads and better access to Linux Lite updates.\n\nOnce your mirror is approved and added, it will appear on our official Mirrors page. For details on how to set up and contribute a mirror, please contact us at our Feedback page.")
mirror_desc.set_wrap(True)
mirror_desc.set_halign(Gtk.Align.CENTER)
mirror_desc.set_justify(Gtk.Justification.CENTER)
mirror_box.append(mirror_desc)
mirror_btn = self.create_button("Learn More", "folder-download-symbolic", "link:https://mityer.khdlhfjijfrupr.tk/feedback.html", "")
mirror_btn.set_halign(Gtk.Align.CENTER)
mirror_box.append(mirror_btn)
mirror_frame.set_child(mirror_box)
self.sections["mirror"] = mirror_frame
content.append(mirror_frame)
# Social Media section
social_frame = Gtk.Frame()
social_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
social_box.add_css_class("section")
social_box.set_margin_top(16)
social_box.set_margin_bottom(16)
social_box.set_margin_start(16)
social_box.set_margin_end(16)
social_title = Gtk.Label()
social_title.set_markup("<span size='large' weight='bold'>? Social Media</span>")
social_title.set_halign(Gtk.Align.CENTER)
social_box.append(social_title)
social_desc = Gtk.Label()
social_desc.set_markup("Social Media has become a very powerful and effective way of proliferating knowledge throughout the world. We encourage you to spread the word about free operating systems such as Linux Lite.")
social_desc.set_wrap(True)
social_desc.set_halign(Gtk.Align.CENTER)
social_desc.set_justify(Gtk.Justification.CENTER)
social_box.append(social_desc)
# Social media buttons
social_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
social_buttons.set_halign(Gtk.Align.CENTER)
social_links = [
("social-facebook.png", "https://www.facebook.com/linuxliteos", "Facebook"),
("social-twitter.png", "https://twitter.com/LinuxLite", "Twitter"),
("social-discord.png", "https://discord.gg/bQSFaFAUkm", "Discord"),
("social-instagram.png", "https://www.instagram.com/linuxliteos", "Instagram"),
("social-linkedin.png", "https://www.linkedin.com/in/jerrybezencon", "LinkedIn"),
("youtube.svg", "https://www.youtube.com/c/linuxliteos", "YouTube"),
("social-reddit.svg", "https://www.reddit.com/r/LinuxLite/", "Reddit"),
]
for img_file, url, tooltip in social_links:
btn = Gtk.Button()
btn.add_css_class("flat")
btn.set_tooltip_text(tooltip)
img_path = os.path.join(self.data_path, "img", img_file)
icon = load_image_from_file(img_path, width=32, height=32)
if icon:
btn.set_child(icon)
else:
btn.set_label(tooltip[0])
btn.connect("clicked", lambda b, u=url: self.cmd_handler.open_url(u))
social_buttons.append(btn)
social_box.append(social_buttons)
social_frame.set_child(social_box)
self.sections["social"] = social_frame
content.append(social_frame)
# Tip box
tip_frame = Gtk.Frame()
tip_frame.add_css_class("success")
tip_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
tip_box.set_margin_top(12)
tip_box.set_margin_bottom(12)
tip_box.set_margin_start(12)
tip_box.set_margin_end(12)
tip_label = Gtk.Label()
tip_label.set_markup("<b>TIP:</b> Social Media is a great way to spread the word about free operating systems such as Linux Lite. If you've come to Linux for the first time, share your experiences with the people you know online via Facebook, Twitter, Discord, Instagram, LinkedIn, YouTube, Reddit, and other Social Networking sites.")
tip_label.set_wrap(True)
tip_label.set_halign(Gtk.Align.CENTER)
tip_label.set_justify(Gtk.Justification.CENTER)
tip_box.append(tip_label)
tip_frame.set_child(tip_box)
content.append(tip_frame)
self.scrolled.set_child(content)
self.append(self.scrolled)
def scroll_to_section(self, section_name):
if section_name not in self.sections:
return
self.pending_scroll = section_name
GLib.idle_add(self._perform_scroll)
def _perform_scroll(self):
if not hasattr(self, 'pending_scroll') or not self.pending_scroll:
return False
section_name = self.pending_scroll
self.pending_scroll = None
if section_name not in self.sections:
return False
target = self.sections[section_name]
content = self.scrolled.get_child()
if hasattr(content, 'get_child'):
content = content.get_child()
y_pos = 0
child = content.get_first_child() if content else None
while child:
if child == target:
break
h = child.get_height()
if h <= 0:
h = child.get_preferred_size().minimum_size.height
y_pos += h + 24
child = child.get_next_sibling()
adj = self.scrolled.get_vadjustment()
adj.set_value(y_pos)
return False
class LiteWelcomeApp(Adw.Application):
def __init__(self):
super().__init__(application_id=APP_ID, flags=Gio.ApplicationFlags.FLAGS_NONE)
self.window = None
self.config = WelcomeConfig()
self.cmd_handler = CommandHandler(self) # Pass app reference for toast notifications
self.stack = None
self.toast_overlay = None # For progress feedback
self.css_provider = None # For theme switching
here = os.path.dirname(os.path.abspath(__file__))
if os.path.exists('/usr/share/litewelcome'):
self.data_path = '/usr/share/litewelcome'
else:
self.data_path = here
def load_css(self):
"""Load custom CSS for the application based on system theme."""
# Get the style manager to detect system color scheme
style_manager = Adw.StyleManager.get_default()
is_dark = style_manager.get_dark()
# Connect to color scheme changes
style_manager.connect("notify::dark", self._on_color_scheme_changed)
self._apply_css(is_dark)
def _on_color_scheme_changed(self, style_manager, param):
"""Called when system color scheme changes."""
is_dark = style_manager.get_dark()
self._apply_css(is_dark)
def _apply_css(self, is_dark):
"""Apply CSS based on whether dark mode is active."""
# Remove old CSS provider if exists
if self.css_provider:
Gtk.StyleContext.remove_provider_for_display(
Gdk.Display.get_default(),
self.css_provider
)
if is_dark:
css = b"""
/* Dark mode - dark backgrounds */
window, .background {
background-color: #2d2d2d;
}
/* Content area */
scrolledwindow, viewport {
background-color: #2d2d2d;
}
/* Frames for sections */
frame {
background-color: #3d3d3d;
border-radius: 8px;
border: 1px solid #505050;
}
/* Header bar styling */
headerbar {
background-color: #383838;
}
/* Footer/toolbar area */
.toolbar {
background-color: #383838;
border-top: 1px solid #505050;
}
/* Tip boxes with green tint (darker) */
frame.success {
background-color: #2d4a32;
border: 1px solid #4a7c54;
}
/* Base button styling with hover effects for all non-flat buttons */
button:not(.flat) {
transition: background-color 150ms ease-in-out;
}
/* Suggested action buttons (blue) */
button.suggested-action {
background-color: @accent_bg_color;
color: @accent_fg_color;
}
button.suggested-action:hover {
background-color: shade(@accent_bg_color, 1.15);
}
button.suggested-action:active {
background-color: shade(@accent_bg_color, 1.25);
}
/* Destructive action buttons (red) */
button.destructive-action {
background-color: @destructive_bg_color;
color: @destructive_fg_color;
}
button.destructive-action:hover {
background-color: shade(@destructive_bg_color, 1.15);
}
button.destructive-action:active {
background-color: shade(@destructive_bg_color, 1.25);
}
/* Regular buttons (no specific class) */
button:not(.flat):not(.suggested-action):not(.destructive-action):not(.image-button) {
background-color: #505050;
}
button:not(.flat):not(.suggested-action):not(.destructive-action):not(.image-button):hover {
background-color: #606060;
}
button:not(.flat):not(.suggested-action):not(.destructive-action):not(.image-button):active {
background-color: #707070;
}
/* Flat buttons (social icons) - subtle hover */
button.flat:hover {
background-color: rgba(255, 255, 255, 0.1);
}
button.flat:active {
background-color: rgba(255, 255, 255, 0.2);
}
/* Social button size */
.social-button {
min-width: 28px;
min-height: 28px;
padding: 4px;
}
/* Tooltip styling - ensure readable text */
tooltip {
background-color: #4a4a4a;
color: #ffffff;
}
tooltip label {
color: #ffffff;
}
"""
else:
css = b"""
/* Light mode - light grey for good contrast */
window, .background {
background-color: #f0f0f0;
}
/* Content area */
scrolledwindow, viewport {
background-color: #f0f0f0;
}
/* Frames for sections */
frame {
background-color: #ffffff;
border-radius: 8px;
border: 1px solid #d0d0d0;
}
/* Header bar styling */
headerbar {
background-color: #e8e8e8;
}
/* Footer/toolbar area */
.toolbar {
background-color: #e8e8e8;
border-top: 1px solid #d0d0d0;
}
/* Tip boxes with green tint */
frame.success {
background-color: #e8f5e9;
border: 1px solid #a5d6a7;
}
/* Base button styling with hover effects for all non-flat buttons */
button:not(.flat) {
transition: background-color 150ms ease-in-out;
}
/* Suggested action buttons (blue) */
button.suggested-action {
background-color: @accent_bg_color;
color: @accent_fg_color;
}
button.suggested-action:hover {
background-color: shade(@accent_bg_color, 0.85);
}
button.suggested-action:active {
background-color: shade(@accent_bg_color, 0.75);
}
/* Destructive action buttons (red) */
button.destructive-action {
background-color: @destructive_bg_color;
color: @destructive_fg_color;
}
button.destructive-action:hover {
background-color: shade(@destructive_bg_color, 0.85);
}
button.destructive-action:active {
background-color: shade(@destructive_bg_color, 0.75);
}
/* Regular buttons (no specific class) */
button:not(.flat):not(.suggested-action):not(.destructive-action):not(.image-button) {
background-color: #e0e0e0;
}
button:not(.flat):not(.suggested-action):not(.destructive-action):not(.image-button):hover {
background-color: #c8c8c8;
}
button:not(.flat):not(.suggested-action):not(.destructive-action):not(.image-button):active {
background-color: #b0b0b0;
}
/* Flat buttons (social icons) - subtle hover */
button.flat:hover {
background-color: rgba(0, 0, 0, 0.08);
}
button.flat:active {
background-color: rgba(0, 0, 0, 0.15);
}
/* Social button size */
.social-button {
min-width: 28px;
min-height: 28px;
padding: 4px;
}
/* Tooltip styling - ensure black text on light background */
tooltip {
background-color: #f5f5f5;
color: #000000;
}
tooltip label {
color: #000000;
}
"""
self.css_provider = Gtk.CssProvider()
self.css_provider.load_from_data(css)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(),
self.css_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
def do_activate(self):
if not self.window:
self.load_css()
self.window = Adw.ApplicationWindow(application=self)
self.window.set_title(APP_NAME)
self.window.set_default_size(850, 768)
# Set taskbar icon by adding search path and using icon name
icon_theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
icon_theme.add_search_path("/usr/share/litewelcome/img")
self.window.set_icon_name("litewelcome")
self.build_ui()
# Center window on screen after it's realized
self.window.connect("realize", self._on_window_realize)
self.window.present()
def _on_window_realize(self, window):
"""Called when window is realized - schedule centering."""
# Use idle_add to ensure the window is fully mapped before centering
GLib.idle_add(self._center_window_on_screen)
def _center_window_on_screen(self):
"""Center the window on the primary monitor."""
try:
display = Gdk.Display.get_default()
if not display:
return False
# Get list of monitors
monitors = display.get_monitors()
if monitors.get_n_items() == 0:
return False
# Get the primary/first monitor
monitor = monitors.get_item(0)
geometry = monitor.get_geometry()
# Window dimensions (use default size)
win_width = 850
win_height = 768
# Calculate center position
x = geometry.x + (geometry.width - win_width) // 2
y = geometry.y + (geometry.height - win_height) // 2
# Try X11-specific positioning (Linux Lite uses X11/Xfce)
if HAS_X11:
surface = self.window.get_surface()
if surface and isinstance(surface, GdkX11.X11Surface):
xid = surface.get_xid()
x11_display = display.get_xdisplay()
if x11_display and xid:
# Use ctypes to call XMoveWindow directly
import ctypes
try:
xlib = ctypes.CDLL('libX11.so.6')
xlib.XMoveWindow(x11_display, xid, x, y)
xlib.XFlush(x11_display)
except (OSError, AttributeError):
pass
except Exception as e:
print(f"Could not center window: {e}")
return False # Don't repeat the idle callback
def build_ui(self):
# Create toast overlay for progress feedback
self.toast_overlay = Adw.ToastOverlay()
main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
header = Adw.HeaderBar()
header.set_show_end_title_buttons(True)
self.back_button = Gtk.Button.new_from_icon_name("go-previous-symbolic")
self.back_button.set_tooltip_text("Back to Home")
self.back_button.connect("clicked", self.on_back_clicked)
self.back_button.set_visible(False)
header.pack_start(self.back_button)
title = Adw.WindowTitle.new(APP_NAME, "")
header.set_title_widget(title)
main_box.append(header)
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
self.stack.set_transition_duration(250)
self.stack.set_vexpand(True)
self.stack.add_named(HomePage(self, self.data_path), "home")
self.stack.add_named(StartPage(self, self.data_path), "start")
self.stack.add_named(SupportPage(self, self.data_path), "support")
self.stack.add_named(ContributePage(self, self.data_path), "contribute")
main_box.append(self.stack)
main_box.append(self.create_footer())
# Wrap main content in toast overlay
self.toast_overlay.set_child(main_box)
self.window.set_content(self.toast_overlay)
def show_toast(self, message, timeout=2):
"""Show a toast notification with the given message."""
if self.toast_overlay:
toast = Adw.Toast.new(message)
toast.set_timeout(timeout)
self.toast_overlay.add_toast(toast)
def create_footer(self):
footer = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
footer.add_css_class("toolbar")
footer.set_margin_start(16)
footer.set_margin_end(16)
footer.set_margin_top(8)
footer.set_margin_bottom(8)
social_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
# Social media buttons with images - larger for better click targets
social_links = [
("social-facebook.png", "https://www.facebook.com/linuxliteos", "Facebook"),
("gitlab.svg", "https://gitlab.com/linuxlite", "GitLab"),
("social-twitter.png", "https://twitter.com/LinuxLite", "Twitter"),
("youtube.svg", "https://www.youtube.com/c/linuxliteos", "YouTube"),
("social-reddit.svg", "https://www.reddit.com/r/LinuxLite/", "Reddit"),
("social-discord.png", "https://discord.gg/bQSFaFAUkm", "Discord"),
]
for img_file, url, tooltip in social_links:
btn = Gtk.Button()
btn.add_css_class("flat")
btn.add_css_class("social-button") # Custom class for larger click target
btn.set_tooltip_text(tooltip)
# Load the social media icon - larger size (28x28)
img_path = os.path.join(self.data_path, "img", img_file)
icon = load_image_from_file(img_path, width=28, height=28)
if icon:
btn.set_child(icon)
else:
# Fallback to first letter if image fails
btn.set_label(tooltip[0])
btn.connect("clicked", lambda b, u=url: self.cmd_handler.open_url(u))
social_box.append(btn)
footer.append(social_box)
spacer = Gtk.Box()
spacer.set_hexpand(True)
footer.append(spacer)
autostart_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
self.autostart_check = Gtk.CheckButton()
self.autostart_check.set_active(self.config.autostart)
self.autostart_check.connect("toggled", self.on_autostart_toggled)
autostart_box.append(self.autostart_check)
autostart_box.append(Gtk.Label(label="Show this dialog on startup"))
footer.append(autostart_box)
close_btn = Gtk.Button(label="Close")
close_btn.set_margin_start(16)
close_btn.connect("clicked", lambda b: self.quit())
footer.append(close_btn)
return footer
def navigate_to(self, page_spec):
if '#' in page_spec:
page, section = page_spec.split('#', 1)
else:
page = page_spec
section = None
self.stack.set_visible_child_name(page)
self.back_button.set_visible(page != "home")
# If a section was specified, scroll to it
if section:
page_widget = self.stack.get_child_by_name(page)
if hasattr(page_widget, 'scroll_to_section'):
page_widget.scroll_to_section(section)
def on_back_clicked(self, button):
self.stack.set_visible_child_name("home")
self.back_button.set_visible(False)
def on_autostart_toggled(self, check):
self.config.toggle_autostart()
check.set_active(self.config.autostart)
def main():
signal.signal(signal.SIGINT, lambda s, f: sys.exit(0))
return LiteWelcomeApp().run(sys.argv)
if __name__ == '__main__':
sys.exit(main())01-04-2026, 02:56 PM
I replaced the code in the /bin/lite-welcome file with the code provided and then the app wouldn't open at all. I noticed that the new code is around 300 lines longer (aprox 1600 lines) than the old code (aprox. 1300 lines). Was the new code supposed to be that different than the previous?
Thanks
Thanks
01-05-2026, 03:59 AM
Yes, different code. Make sure the application file is executable.
01-05-2026, 12:41 PM
Ok, it's working now. I'm not sure why it didn't work the first time because I had made the file executable.Thanks![attachment=303 Wrote:valtam pid='62774' dateline='1767585564']Yes, different code. Make sure the application file is executable.
01-05-2026, 08:00 PM
No problem 

01-06-2026, 12:44 PM
Should the included version of GIMP be updated to 3.x series?
The current version of GIMP that is included with LL is 2.10.36. The 3.x version has been available for quite awhile now and 3.06 is the default on the gimp.org website. When you open in LL and go to "Help > About" it recommends updating.
https://www.gimp.org/downloads
The current version of GIMP that is included with LL is 2.10.36. The 3.x version has been available for quite awhile now and 3.06 is the default on the gimp.org website. When you open in LL and go to "Help > About" it recommends updating.
https://www.gimp.org/downloads
01-06-2026, 03:27 PM
The Flatpack works OK. There is no native complete package for Ubuntu 24. Even the PPA has limitations.
https://ubuntuhandbook.org/index.php/202...ll-ubuntu/
TC
https://ubuntuhandbook.org/index.php/202...ll-ubuntu/
TC
01-06-2026, 11:14 PM
I added the ubuntuhandbook1/gimp-3 PPA and it upgraded fine that way afterwards but it doesn't sound like it's as up-to-date as the flatpak or snap versions so maybe not the best way to go but it's nice that it will updates with the rest of the system via "apt update" etc.
Option 3: Ubuntu PPA (unofficial)
For those who prefer the native
package format, I’ve built GIMP 3.0 into this unofficial PPA for Ubuntu 22.04, Ubuntu 24.04, and Ubuntu 24.10 on
,
/
platforms.
NOTE: The PPA package has few downsides:
to open up a terminal window, and run command:
sudo add-apt-repository ppa:ubuntuhandbook1/gimp-3
Option 3: Ubuntu PPA (unofficial)
For those who prefer the native
Code:
.debCode:
amd64Code:
arm64Code:
armhfNOTE: The PPA package has few downsides:
- It’s built with system GTK3 library, which is a bit outdated. While, GIMP 3.0 relies on the most recent GTK3 library for some fixes.
- Also due to outdated library (libglib2.0-dev), the Ubuntu 22.04 package reversed this commit, or it won’t build.
- For Ubuntu 22.04, the PPA also contains updated versions of libheif, libwebp, kvazaar HEVC encoder that might conflict to other 3rd party packages.
Code:
Ctrl+Alt+Tsudo add-apt-repository ppa:ubuntuhandbook1/gimp-3
