kr580 2.0.0

Desktop KR580VM80 / Intel 8080 emulator.
Documentation
use super::super::{PrinterSetupDialog, PrinterSetupDropdown, PrinterSetupFocus};
use crate::app::{DesktopApp, Message};
use iced::Task;
use k580_ui::devices::printer::PrinterOrientation;

impl DesktopApp {
    pub(super) fn toggle_printer_setup_dropdown(&mut self, dropdown: PrinterSetupDropdown) {
        let Some(dialog) = self.printer_setup_dialog.as_mut() else {
            return;
        };
        dialog.focus = dropdown_focus(dropdown);
        if dialog.open_dropdown == Some(dropdown) {
            close_dropdown(dialog);
            return;
        }
        dialog.dropdown_highlight = selected_dropdown_index(dialog, dropdown);
        dialog.open_dropdown = Some(dropdown);
    }

    pub(super) fn close_printer_setup_dropdown(&mut self) {
        if let Some(dialog) = self.printer_setup_dialog.as_mut() {
            close_dropdown(dialog);
        }
    }

    pub(super) fn move_printer_setup_dropdown_highlight(&mut self, direction: i32) {
        let Some(dialog) = self.printer_setup_dialog.as_mut() else {
            return;
        };
        let Some(dropdown) = dialog.open_dropdown else {
            return;
        };
        let count = dropdown_option_count(dialog, dropdown);
        if count == 0 {
            return;
        }
        let current = dialog
            .dropdown_highlight
            .or_else(|| selected_dropdown_index(dialog, dropdown))
            .unwrap_or(0) as i32;
        dialog.dropdown_highlight = Some((current - direction).clamp(0, count as i32 - 1) as usize);
    }

    pub(super) fn cycle_printer_setup_focus(&mut self, backward: bool) {
        let Some(dialog) = self.printer_setup_dialog.as_mut() else {
            return;
        };
        close_dropdown(dialog);
        dialog.focus_visible = true;
        let current = PrinterSetupFocus::ALL
            .iter()
            .position(|candidate| *candidate == dialog.focus)
            .unwrap_or(0);
        let step = if backward {
            PrinterSetupFocus::ALL.len() - 1
        } else {
            1
        };
        for offset in 1..=PrinterSetupFocus::ALL.len() {
            let index = (current + offset * step) % PrinterSetupFocus::ALL.len();
            let candidate = PrinterSetupFocus::ALL[index];
            if focus_enabled(dialog, candidate) {
                dialog.focus = candidate;
                break;
            }
        }
    }

    pub(super) fn activate_printer_setup_focus(&mut self) -> Task<Message> {
        if let Some(dialog) = self.printer_setup_dialog.as_mut() {
            dialog.focus_visible = false;
        }
        let Some(dialog) = self.printer_setup_dialog.as_ref() else {
            return Task::none();
        };
        if let Some(message) = highlighted_dropdown_message(dialog) {
            return Task::done(message);
        }
        let focus = dialog.focus;
        if !focus_enabled(dialog, focus) {
            return Task::none();
        }
        Task::done(match focus {
            PrinterSetupFocus::Close | PrinterSetupFocus::Cancel => Message::ClosePrinterSetup,
            PrinterSetupFocus::Printer => {
                Message::PrinterSetupDropdownToggled(PrinterSetupDropdown::Printer)
            }
            PrinterSetupFocus::Properties => Message::PrinterSetupProperties,
            PrinterSetupFocus::Paper => {
                Message::PrinterSetupDropdownToggled(PrinterSetupDropdown::Paper)
            }
            PrinterSetupFocus::Source => {
                Message::PrinterSetupDropdownToggled(PrinterSetupDropdown::Source)
            }
            PrinterSetupFocus::Portrait => {
                Message::PrinterSetupOrientationSelected(PrinterOrientation::Portrait)
            }
            PrinterSetupFocus::Landscape => {
                Message::PrinterSetupOrientationSelected(PrinterOrientation::Landscape)
            }
            PrinterSetupFocus::Ok => Message::PrinterSetupConfirmed,
        })
    }
}

fn close_dropdown(dialog: &mut PrinterSetupDialog) {
    dialog.open_dropdown = None;
    dialog.dropdown_highlight = None;
}

fn dropdown_focus(dropdown: PrinterSetupDropdown) -> PrinterSetupFocus {
    match dropdown {
        PrinterSetupDropdown::Printer => PrinterSetupFocus::Printer,
        PrinterSetupDropdown::Paper => PrinterSetupFocus::Paper,
        PrinterSetupDropdown::Source => PrinterSetupFocus::Source,
    }
}

fn dropdown_option_count(dialog: &PrinterSetupDialog, dropdown: PrinterSetupDropdown) -> usize {
    match dropdown {
        PrinterSetupDropdown::Printer => dialog.printers.len(),
        PrinterSetupDropdown::Paper => dialog
            .configuration
            .as_ref()
            .map_or(0, |configuration| configuration.papers.len()),
        PrinterSetupDropdown::Source => dialog
            .configuration
            .as_ref()
            .map_or(0, |configuration| configuration.sources.len()),
    }
}

fn selected_dropdown_index(
    dialog: &PrinterSetupDialog,
    dropdown: PrinterSetupDropdown,
) -> Option<usize> {
    match dropdown {
        PrinterSetupDropdown::Printer => {
            let selected = dialog.selected_name.as_deref()?;
            dialog
                .printers
                .iter()
                .position(|printer| printer.name == selected)
        }
        PrinterSetupDropdown::Paper => {
            let configuration = dialog.configuration.as_ref()?;
            let selected = configuration.settings.paper_id?;
            configuration
                .papers
                .iter()
                .position(|paper| paper.id == selected)
        }
        PrinterSetupDropdown::Source => {
            let configuration = dialog.configuration.as_ref()?;
            let selected = configuration.settings.source_id?;
            configuration
                .sources
                .iter()
                .position(|source| source.id == selected)
        }
    }
}

fn highlighted_dropdown_message(dialog: &PrinterSetupDialog) -> Option<Message> {
    let dropdown = dialog.open_dropdown?;
    let index = dialog
        .dropdown_highlight
        .or_else(|| selected_dropdown_index(dialog, dropdown))?;
    match dropdown {
        PrinterSetupDropdown::Printer => dialog
            .printers
            .get(index)
            .map(|printer| Message::PrinterSetupSelected(printer.name.clone())),
        PrinterSetupDropdown::Paper => dialog
            .configuration
            .as_ref()?
            .papers
            .get(index)
            .map(|paper| Message::PrinterSetupPaperSelected(paper.id)),
        PrinterSetupDropdown::Source => dialog
            .configuration
            .as_ref()?
            .sources
            .get(index)
            .map(|source| Message::PrinterSetupSourceSelected(source.id)),
    }
}

fn focus_enabled(dialog: &PrinterSetupDialog, focus: PrinterSetupFocus) -> bool {
    let configuration_ready = dialog.configuration.is_some() && !dialog.configuration_loading;
    match focus {
        PrinterSetupFocus::Close | PrinterSetupFocus::Cancel => true,
        PrinterSetupFocus::Printer => !dialog.loading && !dialog.printers.is_empty(),
        PrinterSetupFocus::Properties => configuration_ready && !dialog.properties_pending,
        PrinterSetupFocus::Paper => {
            configuration_ready
                && dialog
                    .configuration
                    .as_ref()
                    .is_some_and(|configuration| !configuration.papers.is_empty())
        }
        PrinterSetupFocus::Source => {
            configuration_ready
                && dialog
                    .configuration
                    .as_ref()
                    .is_some_and(|configuration| !configuration.sources.is_empty())
        }
        PrinterSetupFocus::Portrait | PrinterSetupFocus::Landscape => configuration_ready,
        PrinterSetupFocus::Ok => configuration_ready && !dialog.properties_pending,
    }
}