kr580 2.0.0

Desktop KR580VM80 / Intel 8080 emulator.
Documentation
use std::time::Instant;

use super::constants::SETTINGS_SEARCH_INPUT_ID;
use super::messages::{Message, SpeedTier};
use super::settings_modal::SettingsDialog;
use super::settings_modal::{FooterFocus, ResetConfirmFocus, SettingsCategory, SettingsSection};
use super::state::DesktopApp;
use crate::i18n::Key;
use crate::settings_storage::{
    default_lang, language_from_lang, load_settings, preset_from_speed_tier, save_settings,
};
use iced::Task;

impl DesktopApp {
    pub(super) fn dispatch_settings_message(&mut self, message: Message) -> Option<Task<Message>> {
        if let Some(task) = self.dispatch_shortcut_settings_message(&message) {
            return Some(task);
        }
        match message {
            Message::OpenSettings => {
                self.settings_saved_notice = None;
                self.close_top_menu();
                self.hide_opcode_dropdown();
                self.close_open_device_panel();
                let settings = load_settings();
                let mut dialog = SettingsDialog::new_with_shortcuts_and_printer(
                    self.lang,
                    self.default_speed,
                    self.color_scheme,
                    self.follow_pc,
                    self.memory_operand_highlighting,
                    settings.general.floppy_image_path,
                    settings.general.hdd_directory,
                    settings.general.printer_settings,
                    settings.general.printer_dialog_mode,
                    settings.network,
                    settings.shortcuts,
                );
                dialog.original_active_speed = self.speed_tier;
                self.settings_dialog = Some(dialog);
                Some(Task::none())
            }
            Message::CloseSettings => {
                self.settings_saved_notice = None;
                if let Some(dialog) = self.settings_dialog.take() {
                    let lang_changed = self.lang != dialog.original_lang;
                    self.lang = dialog.original_lang;
                    self.color_scheme = dialog.original_color_scheme;
                    let speed_changed = self.default_speed != dialog.original_speed
                        || self.speed_tier != dialog.original_active_speed;
                    self.default_speed = dialog.original_speed;
                    self.follow_pc = dialog.original_follow_pc;
                    self.memory_operand_highlighting = dialog.original_memory_operand_highlighting;
                    self.printer_dialog_mode = dialog.original_printer_dialog_mode;
                    self.shortcut_settings = dialog.original_shortcuts;
                    if speed_changed {
                        self.apply_speed_tier(dialog.original_active_speed);
                    }
                    if lang_changed {
                        self.refresh_localized_status();
                    }
                }
                Some(Task::none())
            }
            Message::SaveSettings => {
                let previous_notice = self.settings_saved_notice.take();
                let Some(dialog) = self.settings_dialog.as_ref() else {
                    return Some(Task::none());
                };
                let network = match parse_network_defaults(dialog) {
                    Ok(network) => network,
                    Err(_) => {
                        let error = self
                            .lang
                            .t(Key::Network(
                                crate::i18n::NetworkKey::GeneralSettingsInvalid,
                            ))
                            .to_owned();
                        if let Some(dialog) = self.settings_dialog.as_mut() {
                            dialog.network_error = Some(error);
                        }
                        return Some(Task::none());
                    }
                };
                self.save_settings_dialog(dialog, network);
                self.commit_settings_dialog_state();
                let started_at = Instant::now();
                self.settings_saved_notice = Some(match previous_notice {
                    Some(notice) => notice.restarted(started_at),
                    None => super::SettingsSavedNotice::new(started_at),
                });
                Some(Task::none())
            }
            Message::SettingsCategorySelected(category) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.category = category;
                    dialog.sidebar_focus = category;
                    dialog.content_focus = Some(dialog.first_content_focus());
                    dialog.keyboard_focus_visible = false;
                    if category != SettingsCategory::Shortcuts
                        && dialog.footer_focus == FooterFocus::ShortcutReset
                    {
                        dialog.footer_focus = FooterFocus::Cancel;
                    }
                    dialog.recording_shortcut = None;
                }
                Some(Task::none())
            }
            Message::SettingsSearchChanged(query) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.search = query;
                    dialog.language_dropdown_open = false;
                    dialog.dropdown_highlight = None;
                    dialog.recording_shortcut = None;
                }
                Some(Task::none())
            }
            Message::SettingsDraftLanguageChanged(lang) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_lang = lang;
                    dialog.language_dropdown_open = false;
                    dialog.dropdown_highlight = None;
                }
                self.lang = lang;
                self.refresh_localized_status();
                Some(Task::none())
            }
            Message::SettingsDraftSpeedChanged(tier) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_speed = tier;
                }
                self.default_speed = tier;
                self.apply_speed_tier(tier);
                Some(Task::none())
            }
            Message::SettingsDraftFollowPcSet(value) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_follow_pc = value;
                }
                self.follow_pc = value;
                Some(Task::none())
            }
            Message::SettingsDraftMemoryOperandHighlightingSet(value) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_memory_operand_highlighting = value;
                }
                self.memory_operand_highlighting = value;
                Some(Task::none())
            }
            Message::SettingsDraftColorSchemeChanged(scheme) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_color_scheme = scheme;
                }
                self.color_scheme = scheme;
                Some(Task::none())
            }
            Message::SettingsDraftPrinterDialogModeSet(mode) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_printer_dialog_mode = mode;
                }
                self.printer_dialog_mode = mode;
                Some(Task::none())
            }
            Message::SettingsFloppyImageBrowse => Some(self.browse_settings_floppy_image()),
            Message::SettingsDraftFloppyImageSet(path) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_floppy_image_path = Some(path);
                }
                Some(Task::none())
            }
            Message::SettingsFloppyImageClear => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_floppy_image_path = None;
                }
                Some(Task::none())
            }
            Message::SettingsHddDirectoryBrowse => Some(self.browse_settings_hdd_directory()),
            Message::SettingsDraftHddDirectorySet(path) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_hdd_directory = Some(path);
                }
                Some(Task::none())
            }
            Message::SettingsPrinterSetup => Some(self.configure_printer_settings()),
            Message::SettingsPrinterSetupFinished(result) => {
                self.finish_printer_settings_setup(result);
                Some(Task::none())
            }
            Message::SettingsPrinterClear => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_printer_settings = None;
                }
                Some(Task::none())
            }
            Message::SettingsNetworkClientHostChanged(host) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_network_client_host = host;
                    dialog.network_error = None;
                }
                Some(Task::none())
            }
            Message::SettingsNetworkClientPortChanged(port) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_network_client_port = port;
                    dialog.network_error = None;
                }
                Some(Task::none())
            }
            Message::SettingsNetworkServerHostChanged(host) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_network_server_host = host;
                    dialog.network_error = None;
                }
                Some(Task::none())
            }
            Message::SettingsNetworkServerPortChanged(port) => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_network_server_port = port;
                    dialog.network_error = None;
                }
                Some(Task::none())
            }
            Message::SettingsLanguageDropdownToggled => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.language_dropdown_open = !dialog.language_dropdown_open;
                    dialog.recording_shortcut = None;
                    dialog.dropdown_highlight = if dialog.language_dropdown_open {
                        Some(dialog.draft_lang)
                    } else {
                        None
                    };
                }
                Some(Task::none())
            }
            Message::SettingsSectionCycle { backward } => {
                let Some(dialog) = self.settings_dialog.as_mut() else {
                    return Some(Task::none());
                };
                if dialog.reset_confirm_open {
                    return Some(Task::none());
                }
                cycle_section(dialog, backward);
                let target = dialog.section;
                Some(match target {
                    SettingsSection::Search => {
                        iced::widget::operation::focus(SETTINGS_SEARCH_INPUT_ID)
                    }
                    _ => iced::widget::operation::focus("settings-blur"),
                })
            }
            Message::SettingsResetRequested => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.reset_confirm_open = true;
                    dialog.reset_confirm_focus = ResetConfirmFocus::Cancel;
                    dialog.reset_confirm_keyboard_focus_visible = false;
                    dialog.language_dropdown_open = false;
                    dialog.dropdown_highlight = None;
                    dialog.recording_shortcut = None;
                }
                Some(Task::none())
            }
            Message::SettingsResetCancelled => {
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.reset_confirm_open = false;
                    dialog.reset_confirm_keyboard_focus_visible = false;
                }
                Some(Task::none())
            }
            Message::SettingsResetConfirmed => {
                let default_lang = default_lang();
                let default_speed = SpeedTier::High;
                let default_color_scheme = crate::persistence::ColorScheme::DEFAULT;
                let default_follow_pc = false;
                let default_memory_operand_highlighting = true;
                let default_printer_dialog_mode = crate::persistence::PrinterDialogMode::default();
                let network = crate::persistence::NetworkSettings::default();
                let shortcuts = crate::persistence::ShortcutSettings::default();
                if let Some(dialog) = self.settings_dialog.as_mut() {
                    dialog.draft_lang = default_lang;
                    dialog.draft_speed = default_speed;
                    dialog.draft_color_scheme = default_color_scheme;
                    dialog.draft_floppy_image_path = None;
                    dialog.draft_hdd_directory = None;
                    dialog.draft_printer_settings = None;
                    dialog.draft_printer_dialog_mode = default_printer_dialog_mode;
                    dialog.original_lang = default_lang;
                    dialog.original_speed = default_speed;
                    dialog.original_active_speed = default_speed;
                    dialog.original_color_scheme = default_color_scheme;
                    dialog.draft_network_client_host = network.host;
                    dialog.draft_network_client_port = network.port.to_string();
                    dialog.draft_network_server_host = network.bind_host;
                    dialog.draft_network_server_port = network.bind_port.to_string();
                    dialog.draft_shortcuts = shortcuts.clone();
                    dialog.original_shortcuts = shortcuts.clone();
                    dialog.recording_shortcut = None;
                    dialog.draft_follow_pc = default_follow_pc;
                    dialog.draft_memory_operand_highlighting = default_memory_operand_highlighting;
                    dialog.original_follow_pc = default_follow_pc;
                    dialog.original_memory_operand_highlighting =
                        default_memory_operand_highlighting;
                    dialog.original_printer_dialog_mode = default_printer_dialog_mode;
                    dialog.network_error = None;
                    dialog.reset_confirm_open = false;
                    dialog.reset_confirm_keyboard_focus_visible = false;
                }
                self.follow_pc = default_follow_pc;
                self.memory_operand_highlighting = default_memory_operand_highlighting;
                self.shortcut_settings = shortcuts;
                self.printer_default_settings = None;
                self.printer_dialog_mode = default_printer_dialog_mode;
                let lang_changed = self.lang != default_lang;
                self.lang = default_lang;
                self.default_speed = default_speed;
                self.color_scheme = default_color_scheme;
                self.apply_speed_tier(default_speed);
                if lang_changed {
                    self.refresh_localized_status();
                }
                if let Some(dialog) = self.settings_dialog.as_ref()
                    && let Ok(network) = parse_network_defaults(dialog)
                {
                    self.save_settings_dialog(dialog, network);
                }
                Some(Task::none())
            }
            Message::SettingsFileAssociationRegister => {
                Some(self.update_file_association(k580_ui::file_assoc::register))
            }
            Message::SettingsFileAssociationUnregister => {
                Some(self.update_file_association(k580_ui::file_assoc::unregister))
            }
            _ => None,
        }
    }

    fn update_file_association(&mut self, operation: fn() -> Result<(), String>) -> Task<Message> {
        if let Err(error) = operation() {
            self.show_error_notice(format!("{}: {}", self.lang.t(Key::ErrorPrefix), error));
        }
        self.file_association_toggle_revision =
            self.file_association_toggle_revision.wrapping_add(1);
        Task::none()
    }

    pub(super) fn commit_settings_dialog_state(&mut self) {
        let active_speed = self.speed_tier;
        let Some(dialog) = self.settings_dialog.as_mut() else {
            return;
        };
        self.shortcut_settings = dialog.draft_shortcuts.clone();
        self.printer_default_settings = dialog.draft_printer_settings.clone();
        self.printer_dialog_mode = dialog.draft_printer_dialog_mode;
        dialog.original_lang = dialog.draft_lang;
        dialog.original_speed = dialog.draft_speed;
        dialog.original_active_speed = active_speed;
        dialog.original_color_scheme = dialog.draft_color_scheme;
        dialog.original_follow_pc = dialog.draft_follow_pc;
        dialog.original_memory_operand_highlighting = dialog.draft_memory_operand_highlighting;
        dialog.original_printer_dialog_mode = dialog.draft_printer_dialog_mode;
        dialog.original_shortcuts = dialog.draft_shortcuts.clone();
    }

    fn save_settings_dialog(&self, dialog: &SettingsDialog, network: NetworkDefaults) {
        let mut settings = load_settings();
        settings.general.language = language_from_lang(self.lang);
        settings.general.default_speed = preset_from_speed_tier(self.default_speed);
        settings.general.follow_pc = dialog.draft_follow_pc;
        settings.general.memory_operand_highlighting = dialog.draft_memory_operand_highlighting;
        settings.general.floppy_image_path = dialog.draft_floppy_image_path.clone();
        settings.general.hdd_directory = dialog.draft_hdd_directory.clone();
        settings
            .general
            .set_printer_settings(dialog.draft_printer_settings.clone());
        settings.general.printer_dialog_mode = dialog.draft_printer_dialog_mode;
        settings.ui.theme = dialog.draft_color_scheme;
        apply_network_defaults(&mut settings.network, network);
        settings.shortcuts = dialog.draft_shortcuts.clone();
        save_settings(&settings);
    }
}

mod network;
mod section;
mod shortcuts;
mod storage;
use network::{NetworkDefaults, apply_network_defaults, parse_network_defaults};
use section::cycle_section;