diff --git a/iridium_installer/main.py b/iridium_installer/main.py index f31826b..62aa348 100644 --- a/iridium_installer/main.py +++ b/iridium_installer/main.py @@ -1,3 +1,4 @@ +import argparse import sys import gi @@ -11,21 +12,30 @@ from .ui.window import InstallerWindow class IridiumInstallerApp(Adw.Application): - def __init__(self): + def __init__(self, mock_mode=False): super().__init__( application_id="org.iridium.Installer", flags=Gio.ApplicationFlags.FLAGS_NONE, ) + self.mock_mode = mock_mode def do_activate(self): win = self.props.active_window if not win: - win = InstallerWindow(application=self) + win = InstallerWindow(application=self, mock_mode=self.mock_mode) win.present() def main(): - app = IridiumInstallerApp() + parser = argparse.ArgumentParser(description="Iridium OS Installer") + parser.add_argument( + "--mock", + action="store_true", + help="Run in mock mode (no actual changes to disk)", + ) + args = parser.parse_args() + + app = IridiumInstallerApp(mock_mode=args.mock) return app.run(sys.argv) diff --git a/iridium_installer/ui/pages/user.py b/iridium_installer/ui/pages/user.py index ed94e7d..7b18ba8 100644 --- a/iridium_installer/ui/pages/user.py +++ b/iridium_installer/ui/pages/user.py @@ -1,14 +1,22 @@ +import re + import gi gi.require_version("Gtk", "4.0") gi.require_version("Adw", "1") -from gi.repository import Adw, Gtk +from gi.repository import Adw, GObject, Gtk class UserPage(Adw.Bin): + __gsignals__ = { + "validity-changed": (GObject.SignalFlags.RUN_FIRST, None, (bool,)), + } + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self._is_valid = False + clamp = Adw.Clamp() clamp.set_maximum_size(500) self.set_child(clamp) @@ -32,6 +40,7 @@ class UserPage(Adw.Bin): self.fullname_row.set_title("Full Name") self.fullname_row.set_text("Administrator") self.fullname_row.connect("notify::text", self.on_fullname_changed) + self.fullname_row.connect("notify::text", self.on_input_changed) group.add(self.fullname_row) self.username_row = Adw.EntryRow() @@ -40,14 +49,17 @@ class UserPage(Adw.Bin): self.username_handler_id = self.username_row.connect( "notify::text", self.on_username_changed ) + self.username_row.connect("notify::text", self.on_input_changed) group.add(self.username_row) self.password_row = Adw.PasswordEntryRow() self.password_row.set_title("Password") + self.password_row.connect("notify::text", self.on_input_changed) group.add(self.password_row) self.confirm_row = Adw.PasswordEntryRow() self.confirm_row.set_title("Confirm Password") + self.confirm_row.connect("notify::text", self.on_input_changed) group.add(self.confirm_row) # Hostname @@ -58,10 +70,14 @@ class UserPage(Adw.Bin): self.hostname_row = Adw.EntryRow() self.hostname_row.set_title("Hostname") self.hostname_row.set_text("iridium") + self.hostname_row.connect("notify::text", self.on_input_changed) host_group.add(self.hostname_row) self.username_manually_set = False + # Initial validation + self.validate() + def on_fullname_changed(self, entry, _pspec): if self.username_manually_set: return @@ -113,6 +129,53 @@ class UserPage(Adw.Bin): def on_username_changed(self, entry, _pspec): self.username_manually_set = True + def on_input_changed(self, *args): + self.validate() + + def validate(self): + valid = True + + # Username validation + username = self.username_row.get_text() + if not username: + valid = False + self.username_row.add_css_class("error") + elif not re.match(r"^[a-z_][a-z0-9_-]*$", username): + valid = False + self.username_row.add_css_class("error") + else: + self.username_row.remove_css_class("error") + + # Password validation + pw = self.password_row.get_text() + confirm = self.confirm_row.get_text() + + if not pw: + valid = False + # Don't show error for empty password initially, just invalidate + # It looks a lot nicer like that + + if pw != confirm: + valid = False + self.confirm_row.add_css_class("error") + else: + self.confirm_row.remove_css_class("error") + + # Hostname validation + hostname = self.hostname_row.get_text() + if not hostname: + valid = False + self.hostname_row.add_css_class("error") + else: + self.hostname_row.remove_css_class("error") + + if self._is_valid != valid: + self._is_valid = valid + self.emit("validity-changed", valid) + + def is_valid(self): + return self._is_valid + def get_user_info(self): return { "fullname": self.fullname_row.get_text(), diff --git a/iridium_installer/ui/window.py b/iridium_installer/ui/window.py index 92a8103..abf1daf 100644 --- a/iridium_installer/ui/window.py +++ b/iridium_installer/ui/window.py @@ -8,17 +8,19 @@ from .pages.additional_modules import ModulesPage from .pages.install_mode import InstallModePage from .pages.partitioning import PartitioningPage, calculate_auto_partitions from .pages.storage import StoragePage +from .pages.summary import SummaryPage from .pages.user import UserPage from .pages.welcome import WelcomePage -from .pages.summary import SummaryPage class InstallerWindow(Adw.ApplicationWindow): - def __init__(self, *args, **kwargs): + def __init__(self, mock_mode=False, *args, **kwargs): super().__init__(*args, **kwargs) + self.mock_mode = mock_mode + self.set_default_size(900, 650) - self.set_title("Iridium Installer") + self.set_title("Iridium Installer" + (" (MOCK MODE)" if mock_mode else "")) self.toolbar_view = Adw.ToolbarView() self.set_content(self.toolbar_view) @@ -71,6 +73,7 @@ class InstallerWindow(Adw.ApplicationWindow): self.partitioning_page = PartitioningPage() self.modules_page = ModulesPage() self.user_page = UserPage() + self.user_page.connect("validity-changed", self.on_user_validity_changed) self.summary_page = SummaryPage() # Add Pages @@ -94,6 +97,9 @@ class InstallerWindow(Adw.ApplicationWindow): def on_disk_selected(self, page, device_name): self.update_buttons() + def on_user_validity_changed(self, page, valid): + self.update_buttons() + def update_buttons(self): # Back button state self.back_button.set_sensitive(self.current_page_index > 0) @@ -105,11 +111,15 @@ class InstallerWindow(Adw.ApplicationWindow): forced_selection = True if current_page_name == "storage": forced_selection = self.storage_page.get_selected_disk() is not None + elif current_page_name == "user": + forced_selection = self.user_page.is_valid() self.next_button.set_sensitive(forced_selection) if current_page_name == "summary": - self.next_button.set_label("Install") + self.next_button.set_label( + "Install" + (" (MOCK)" if self.mock_mode else "") + ) self.next_button.add_css_class("destructive-action") self.next_button.remove_css_class("suggested-action") else: @@ -173,30 +183,36 @@ class InstallerWindow(Adw.ApplicationWindow): mode=mode, partitions=partitions_config, user_info=user_info, - modules=modules + modules=modules, ) # Proceed to summary page (which is next_index after user) - + if current_page_name == "summary": # THIS IS THE REAL INSTALL TRIGGER print("Install process triggered!") disk = self.storage_page.get_selected_disk() - print(f"Disk: {disk}") mode = self.install_mode_page.get_mode() - print(f"Mode: {mode}") modules = self.modules_page.get_modules() - print(f"Modules: {modules}") user_info = self.user_page.get_user_info() - print(f"User: {user_info}") - - # Recalculate or retrieve partitions just to be safe/consistent + partitions_config = {} if mode == "manual": partitions_config = self.partitioning_page.get_config() elif mode == "automatic": partitions = calculate_auto_partitions(disk) partitions_config = {"partitions": partitions} - print(f"Partitioning: {partitions_config}") + + if self.mock_mode: + print("!!! MOCK MODE ENABLED - NO CHANGES WILL BE MADE !!!") + print(f"Target Disk: {disk}") + print(f"Mode: {mode}") + print(f"Modules: {modules}") + print(f"User: {user_info}") + print(f"Partition Config: {partitions_config}") + print("Simulation complete.") + else: + print("NOT IMPLEMENTED") + return if next_index < len(self.page_ids): diff --git a/run.sh b/run.sh index 5c451d6..2bff674 100755 --- a/run.sh +++ b/run.sh @@ -1,5 +1,4 @@ #!/bin/bash export GSETTINGS_SCHEMA_DIR=. -echo "Starting Iridium Installer..." -python3 -m iridium_installer.main +python3 -m iridium_installer.main "$@"