kr580 1.1.0

Desktop KR580VM80 / Intel 8080 emulator.
Documentation
mod focus;
mod keyboard;
mod presets;
mod validation;

use super::PrinterPropertiesDialog;
use super::tasks::{
    apply_native_printer_property_blocking, load_native_printer_properties_blocking,
};
use crate::app::{DesktopApp, Message};
use crate::backend::decode_oem_text;
use crate::settings_storage::load_settings;
use iced::Task;
use k580_ui::devices::printer::{PrinterPropertyChange, PrinterPropertySheet};

impl DesktopApp {
    pub(super) fn open_selected_printer_properties(&mut self) -> Task<Message> {
        let Some(dialog) = self.printer_setup_dialog.as_ref() else {
            return Task::none();
        };
        let Some(printer) = dialog.selected_printer().cloned() else {
            return Task::none();
        };
        let Some(settings) = dialog
            .configuration
            .as_ref()
            .map(|configuration| configuration.settings.clone())
        else {
            return Task::none();
        };
        let presets = load_settings()
            .general
            .printer_presets
            .into_iter()
            .filter(|preset| preset.settings.printer_name == printer.name)
            .collect();
        let preview_text = decode_oem_text(&self.snapshot.devices.printer.spool);
        let detached_surface = self.printer_setup_uses_detached_window();
        if let Some(dialog) = self.printer_setup_dialog.as_mut() {
            dialog.properties_pending = true;
            dialog.properties = Some(PrinterPropertiesDialog::new(preview_text, presets));
            dialog.properties_surface_ready = !detached_surface;
        }
        let printer_name = printer.name.clone();
        Task::batch([
            self.open_detached_printer_properties_window(),
            Task::perform(
                load_native_printer_properties_blocking(printer, settings),
                move |result| Message::PrinterPropertiesLoaded {
                    printer_name,
                    result,
                },
            ),
        ])
    }

    pub(super) fn route_printer_properties_message(
        &mut self,
        message: &Message,
    ) -> Option<Task<Message>> {
        match message {
            Message::PrinterPropertiesAttentionRequested => {
                Some(self.request_printer_properties_attention())
            }
            Message::PrinterPropertiesLoaded {
                printer_name,
                result,
            }
            | Message::PrinterPropertyApplied {
                printer_name,
                result,
            } => {
                self.finish_property_sheet_load(printer_name, result.clone());
                Some(Task::none())
            }
            Message::PrinterPropertiesTabSelected(tab) => {
                if let Some(properties) = self.properties_mut() {
                    properties.tab = *tab;
                    properties.focus = super::PrinterPropertiesFocus::Tab(*tab);
                    properties.focus_visible = false;
                    properties.open_dropdown = None;
                    properties.dropdown_highlight = None;
                }
                Some(Task::none())
            }
            Message::PrinterPropertiesFocusResolved { focused, backward } => {
                Some(self.resolve_printer_properties_focus(focused.clone(), *backward))
            }
            Message::PrinterPropertyDropdownToggled(dropdown) => {
                if let Some(properties) = self.properties_mut() {
                    properties.focus = super::PrinterPropertiesFocus::Dropdown(dropdown.clone());
                }
                self.toggle_property_dropdown(dropdown.clone());
                Some(Task::none())
            }
            Message::PrinterPropertyDropdownDismissed(dropdown) => {
                if self
                    .properties()
                    .is_some_and(|properties| properties.open_dropdown.as_ref() == Some(dropdown))
                {
                    self.close_property_dropdown();
                }
                Some(Task::none())
            }
            Message::PrinterPropertyFeatureSelected(change) => {
                self.close_property_dropdown();
                Some(self.apply_printer_property(change.clone()))
            }
            Message::PrinterPropertyParameterChanged { name, value } => {
                if let Some(properties) = self.properties_mut() {
                    properties.focus = super::PrinterPropertiesFocus::ParameterInput(name.clone());
                    properties
                        .parameter_values
                        .insert(name.clone(), value.clone());
                }
                Some(Task::none())
            }
            Message::PrinterPropertyParameterApply(name) => {
                if let Some(properties) = self.properties_mut() {
                    properties.focus = super::PrinterPropertiesFocus::ParameterApply(name.clone());
                }
                Some(self.apply_printer_parameter(name))
            }
            Message::PrinterPropertyPaperSelected(id) => {
                self.close_property_dropdown();
                self.select_property_paper(*id);
                Some(Task::none())
            }
            Message::PrinterPropertySourceSelected(id) => {
                self.close_property_dropdown();
                self.select_property_source(*id);
                Some(Task::none())
            }
            Message::PrinterPropertyOrientationSelected(orientation) => {
                self.close_property_dropdown();
                if let Some(properties) = self.properties_mut() {
                    properties.focus = match orientation {
                        k580_ui::devices::printer::PrinterOrientation::Portrait => {
                            super::PrinterPropertiesFocus::Portrait
                        }
                        k580_ui::devices::printer::PrinterOrientation::Landscape => {
                            super::PrinterPropertiesFocus::Landscape
                        }
                    };
                }
                if let Some(settings) = self.property_settings_mut() {
                    settings.orientation = *orientation;
                }
                Some(Task::none())
            }
            Message::PrinterPropertyPresetSelected(name) => {
                self.close_property_dropdown();
                Some(self.select_printer_preset(name))
            }
            Message::PrinterPropertyPresetNameChanged(name) => {
                if let Some(properties) = self.properties_mut() {
                    properties.focus = super::PrinterPropertiesFocus::PresetName;
                    properties.preset_name = name.clone();
                }
                Some(Task::none())
            }
            Message::PrinterPropertyPresetSave => {
                self.save_printer_preset();
                Some(Task::none())
            }
            Message::PrinterPropertyPresetDelete => {
                self.delete_printer_preset();
                Some(Task::none())
            }
            Message::PrinterPropertyConfirmed => {
                self.confirm_printer_properties();
                Some(self.close_detached_printer_properties_window())
            }
            Message::ClosePrinterProperties => {
                self.close_printer_properties();
                Some(self.close_detached_printer_properties_window())
            }
            Message::EscPressed => {
                if self
                    .properties()
                    .is_some_and(|properties| properties.open_dropdown.is_some())
                {
                    self.close_property_dropdown();
                    Some(Task::none())
                } else {
                    self.close_printer_properties();
                    Some(self.close_detached_printer_properties_window())
                }
            }
            Message::EnterPressed => Some(self.activate_printer_properties_focus()),
            Message::FocusCycle { backward } => {
                Some(self.begin_printer_properties_focus_cycle(*backward))
            }
            Message::ArrowKey(direction) => {
                self.move_property_dropdown_highlight(*direction);
                Some(Task::none())
            }
            Message::MousePressed | Message::MousePressedIgnored => {
                if let Some(properties) = self.properties_mut() {
                    properties.focus_visible = false;
                }
                if matches!(message, Message::MousePressedIgnored) {
                    self.close_property_dropdown();
                }
                Some(Task::none())
            }
            Message::Tick => {
                if let Some(properties) = self.properties_mut() {
                    properties.expire_attention(std::time::Instant::now());
                }
                None
            }
            Message::CursorMoved(_)
            | Message::ModifiersChanged(_)
            | Message::FocusReconciled { .. }
            | Message::ResolveFocusedTracker(_) => None,
            _ => Some(Task::none()),
        }
    }

    fn finish_property_sheet_load(
        &mut self,
        printer_name: &str,
        result: Result<PrinterPropertySheet, String>,
    ) {
        if self
            .printer_setup_dialog
            .as_ref()
            .and_then(|dialog| dialog.selected_name.as_deref())
            != Some(printer_name)
        {
            return;
        }
        let Some(properties) = self.properties_mut() else {
            return;
        };
        properties.loading = false;
        properties.applying = false;
        match result {
            Ok(sheet) => {
                properties.sheet = Some(sheet);
                properties.error = None;
                properties.sync_parameter_values();
            }
            Err(error) => properties.error = Some(error),
        }
    }

    fn apply_printer_property(&mut self, change: PrinterPropertyChange) -> Task<Message> {
        let Some((printer, settings)) = self.property_context() else {
            return Task::none();
        };
        if let Some(properties) = self.properties_mut() {
            if properties.applying {
                return Task::none();
            }
            properties.applying = true;
            properties.error = None;
        }
        let printer_name = printer.name.clone();
        Task::perform(
            apply_native_printer_property_blocking(printer, settings, change),
            move |result| Message::PrinterPropertyApplied {
                printer_name,
                result,
            },
        )
    }

    fn apply_printer_parameter(&mut self, name: &str) -> Task<Message> {
        let lang = self.lang;
        let Some(properties) = self.properties() else {
            return Task::none();
        };
        let Some(sheet) = properties.sheet.as_ref() else {
            return Task::none();
        };
        let Some(parameter) = sheet
            .parameters
            .iter()
            .find(|parameter| parameter.name == name)
        else {
            return Task::none();
        };
        let value = properties
            .parameter_values
            .get(name)
            .cloned()
            .unwrap_or_else(|| parameter.value.clone());
        if let Err(error) =
            validation::validate_parameter(parameter.minimum, parameter.maximum, &value, lang)
        {
            if let Some(properties) = self.properties_mut() {
                properties.error = Some(error);
            }
            return Task::none();
        }
        self.apply_printer_property(PrinterPropertyChange::Parameter {
            parameter_name: parameter.name.clone(),
            value_type: parameter.value_type.clone(),
            value,
        })
    }

    fn select_property_paper(&mut self, id: i16) {
        let Some(sheet) = self
            .properties_mut()
            .and_then(|dialog| dialog.sheet.as_mut())
        else {
            return;
        };
        sheet.configuration.select_paper(id);
    }

    fn select_property_source(&mut self, id: i16) {
        let Some(sheet) = self
            .properties_mut()
            .and_then(|dialog| dialog.sheet.as_mut())
        else {
            return;
        };
        sheet.configuration.select_source(id);
    }

    fn confirm_printer_properties(&mut self) {
        let configuration = self
            .properties()
            .and_then(|properties| properties.sheet.as_ref())
            .map(|sheet| sheet.configuration.clone());
        if let Some(dialog) = self.printer_setup_dialog.as_mut() {
            if let Some(configuration) = configuration {
                dialog.configuration = Some(configuration);
            }
            dialog.properties = None;
            dialog.properties_pending = false;
        }
    }

    pub(super) fn close_printer_properties(&mut self) {
        if let Some(dialog) = self.printer_setup_dialog.as_mut() {
            dialog.properties = None;
            dialog.properties_pending = false;
        }
    }

    fn property_context(
        &self,
    ) -> Option<(
        k580_ui::devices::printer::PrinterInfo,
        k580_ui::devices::printer::PrinterSettings,
    )> {
        let dialog = self.printer_setup_dialog.as_ref()?;
        let printer = dialog.selected_printer()?.clone();
        let settings = dialog
            .properties
            .as_ref()?
            .sheet
            .as_ref()?
            .configuration
            .settings
            .clone();
        Some((printer, settings))
    }

    fn properties(&self) -> Option<&PrinterPropertiesDialog> {
        self.printer_setup_dialog.as_ref()?.properties.as_ref()
    }

    fn properties_mut(&mut self) -> Option<&mut PrinterPropertiesDialog> {
        self.printer_setup_dialog.as_mut()?.properties.as_mut()
    }

    fn close_property_dropdown(&mut self) {
        if let Some(properties) = self.properties_mut() {
            properties.open_dropdown = None;
            properties.dropdown_highlight = None;
        }
    }

    fn property_settings_mut(&mut self) -> Option<&mut k580_ui::devices::printer::PrinterSettings> {
        Some(
            &mut self
                .properties_mut()?
                .sheet
                .as_mut()?
                .configuration
                .settings,
        )
    }
}