use iced::widget::{Space, button, container, row, svg};
use iced::{Background, Border, Color, Element, Length, alignment};
use super::super::consts::TOGGLE_SEGMENT_WIDTH;
use super::super::setting_row::setting_row;
use super::super::speed::segmented_button_width;
use crate::app::{ContentFocus, Message, SettingsDialog, SettingsSection};
use crate::i18n::{Key, Lang};
use crate::view::icons;
use crate::view::theme::{TOKYO_BORDER, TOKYO_MUTED, TOKYO_SURFACE, TOKYO_TEXT, ui_text};
pub(super) fn follow_pc_toggle_row<'a>(
dialog: &'a SettingsDialog,
lang: Lang,
) -> Element<'a, Message> {
let kb_focus = (dialog.section == SettingsSection::Content)
.then_some(dialog.content_focus)
.flatten();
let kb_focused = kb_focus == Some(ContentFocus::FollowPc);
let segments = row![
segmented_button_width(
lang.t(Key::SettingsToggleOn),
dialog.draft_follow_pc,
kb_focused,
Message::SettingsDraftFollowPcSet(true),
TOGGLE_SEGMENT_WIDTH,
),
segmented_button_width(
lang.t(Key::SettingsToggleOff),
!dialog.draft_follow_pc,
false,
Message::SettingsDraftFollowPcSet(false),
TOGGLE_SEGMENT_WIDTH,
),
]
.spacing(6);
setting_row(
lang.t(Key::SettingsFollowPcLabel),
lang.t(Key::SettingsFollowPcHint),
segments.into(),
)
}
pub(super) fn memory_operand_highlighting_row<'a>(
dialog: &'a SettingsDialog,
lang: Lang,
) -> Element<'a, Message> {
let kb_focus = (dialog.section == SettingsSection::Content)
.then_some(dialog.content_focus)
.flatten();
let kb_focused = kb_focus == Some(ContentFocus::MemoryOperandHighlighting);
let segments = row![
segmented_button_width(
lang.t(Key::SettingsToggleOn),
dialog.draft_memory_operand_highlighting,
kb_focused,
Message::SettingsDraftMemoryOperandHighlightingSet(true),
TOGGLE_SEGMENT_WIDTH,
),
segmented_button_width(
lang.t(Key::SettingsToggleOff),
!dialog.draft_memory_operand_highlighting,
false,
Message::SettingsDraftMemoryOperandHighlightingSet(false),
TOGGLE_SEGMENT_WIDTH,
),
]
.spacing(6);
setting_row(
lang.t(Key::SettingsMemoryOperandHighlightingLabel),
lang.t(Key::SettingsMemoryOperandHighlightingHint),
segments.into(),
)
}
pub(super) fn hdd_directory_row<'a>(
dialog: &'a SettingsDialog,
lang: Lang,
) -> Element<'a, Message> {
let kb_focus = (dialog.section == SettingsSection::Content)
.then_some(dialog.content_focus)
.flatten();
let kb_focused = kb_focus == Some(ContentFocus::HddDirectory);
let raw_path = dialog
.draft_hdd_directory
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| {
std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.unwrap_or_else(|_| ".".to_owned())
});
let display = truncate_path(&raw_path, 36);
let browse_btn = settings_browse_button(
lang.t(Key::SettingsHddDirectoryBrowse),
Message::SettingsHddDirectoryBrowse,
kb_focused,
);
let control = row![
Space::new().width(Length::Fill),
ui_text(display, 13, TOKYO_MUTED),
Space::new().width(Length::Fixed(8.0)),
browse_btn,
]
.align_y(alignment::Vertical::Center);
setting_row(
lang.t(Key::SettingsHddDirectoryLabel),
lang.t(Key::SettingsHddDirectoryHint),
control.into(),
)
}
pub(super) fn file_association_row<'a>(
dialog: &'a SettingsDialog,
lang: Lang,
) -> Element<'a, Message> {
let kb_focus = (dialog.section == SettingsSection::Content)
.then_some(dialog.content_focus)
.flatten();
let kb_focused = kb_focus == Some(ContentFocus::FileAssociation);
let registered = k580_ui::file_assoc::is_registered();
let label = if registered {
Key::SettingsFileAssociationRemove
} else {
Key::SettingsFileAssociationAdd
};
let message = if registered {
Message::SettingsFileAssociationUnregister
} else {
Message::SettingsFileAssociationRegister
};
let btn = settings_browse_button(lang.t(label), message, kb_focused);
let control = row![Space::new().width(Length::Fill), btn].align_y(alignment::Vertical::Center);
setting_row(
lang.t(Key::SettingsFileAssociationLabel),
lang.t(Key::SettingsFileAssociationHint),
control.into(),
)
}
pub(super) fn floppy_image_row<'a>(dialog: &'a SettingsDialog, lang: Lang) -> Element<'a, Message> {
let kb_focus = (dialog.section == SettingsSection::Content)
.then_some(dialog.content_focus)
.flatten();
let kb_focused = kb_focus == Some(ContentFocus::FloppyImage);
let path_display = dialog
.draft_floppy_image_path
.as_ref()
.map(|p| truncate_path(&p.display().to_string(), 24))
.unwrap_or_default();
let browse_btn = settings_browse_button(
lang.t(Key::SettingsFloppyImageBrowse),
Message::SettingsFloppyImageBrowse,
kb_focused,
);
let mut control = row![Space::new().width(Length::Fill)];
if dialog.draft_floppy_image_path.is_some() {
control = control.push(ui_text(path_display, 13, TOKYO_MUTED));
control = control.push(Space::new().width(Length::Fixed(8.0)));
}
control = control.push(browse_btn);
if dialog.draft_floppy_image_path.is_some() {
let clear_btn = button(
container(
svg(icons::brush_cleaning())
.width(Length::Fixed(16.0))
.height(Length::Fixed(16.0))
.style(move |_theme, _status| svg::Style {
color: Some(TOKYO_TEXT),
}),
)
.width(Length::Fill)
.height(Length::Fill)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center),
)
.on_press(Message::SettingsFloppyImageClear)
.padding(0)
.width(Length::Fixed(28.0))
.height(Length::Fixed(28.0))
.style(move |_theme, status| {
let bg = match status {
button::Status::Pressed => TOKYO_BORDER,
button::Status::Hovered => TOKYO_SURFACE,
_ => Color::TRANSPARENT,
};
button::Style {
background: Some(Background::Color(bg)),
text_color: TOKYO_TEXT,
border: Border {
radius: 6.0.into(),
width: 1.0,
color: TOKYO_BORDER,
},
..button::Style::default()
}
});
control = control.push(Space::new().width(Length::Fixed(8.0)));
control = control.push(clear_btn);
}
control = control.align_y(alignment::Vertical::Center);
setting_row(
lang.t(Key::SettingsFloppyImageLabel),
lang.t(Key::SettingsFloppyImageHint),
control.into(),
)
}
fn settings_browse_button<'a>(
label: &'static str,
message: Message,
kb_focused: bool,
) -> Element<'a, Message> {
button(container(ui_text(label, 12, TOKYO_TEXT)).padding([2, 8]))
.on_press(message)
.style(move |_theme, status| {
let bg = match status {
button::Status::Pressed => TOKYO_BORDER,
button::Status::Hovered => TOKYO_SURFACE,
_ if kb_focused => TOKYO_SURFACE,
_ => Color::TRANSPARENT,
};
button::Style {
background: Some(Background::Color(bg)),
text_color: TOKYO_TEXT,
border: Border {
radius: 6.0.into(),
width: 1.0,
color: TOKYO_BORDER,
},
..button::Style::default()
}
})
.into()
}
pub(super) fn truncate_path(path: &str, max: usize) -> String {
let chars: Vec<char> = path.chars().collect();
if chars.len() <= max {
return path.to_owned();
}
let sep = if path.contains('\\') { '\\' } else { '/' };
let segments: Vec<&str> = path.split(sep).collect();
if segments.len() < 3 {
let head: String = chars.iter().take(max / 2).collect();
let tail: String = chars.iter().skip(chars.len() - max / 2).collect();
return format!("{head}…{tail}");
}
let first = segments[0];
let last = segments[segments.len() - 1];
let mut budget = max.saturating_sub(first.chars().count() + last.chars().count() + 3);
let mut middle = String::new();
for seg in segments[1..segments.len() - 1].iter().rev() {
let cost = seg.chars().count() + 1;
if cost > budget {
break;
}
middle = format!("{sep}{seg}{middle}");
budget -= cost;
}
if middle.is_empty() {
format!("{first}{sep}…{sep}{last}")
} else {
format!("{first}{sep}…{middle}{sep}{last}")
}
}