use iced::widget::{column, container, row, text_input};
use iced::{Element, Length, Padding, alignment};
use super::icons;
use super::styles::{disabled_input_borderless_style, input_borderless_style, input_shell_style};
use super::theme::{MONO_FONT, TOKYO_BLUE, TOKYO_GREEN, TOKYO_MAGENTA, TOKYO_RED, TOKYO_YELLOW};
use super::tooltips::shortcut_hint;
use super::widgets::{
enter_button, enter_button_disabled, icon_action_button, legend_panel, spinner_text_input,
};
use crate::app::{
DesktopApp, MEMORY_ADDRESS_INPUT_ID, MEMORY_VALUE_INPUT_ID, Message, REGISTER_NAME_INPUT_ID,
REGISTER_VALUE_INPUT_ID,
};
use crate::i18n::Key;
impl DesktopApp {
pub(super) fn side_panel(&self) -> Element<'_, Message> {
column![
self.memory_panel(),
self.memory_editor_panel(),
self.register_editor_panel(),
self.actions_panel(),
]
.spacing(8)
.width(Length::Fixed(330.0))
.height(Length::Fill)
.into()
}
fn memory_editor_panel(&self) -> Element<'_, Message> {
let value_focused = self.focused_input == Some(MEMORY_VALUE_INPUT_ID);
let mut value_text = text_input(
self.input_placeholder(MEMORY_VALUE_INPUT_ID, "00"),
&self.memory_value_input,
)
.id(MEMORY_VALUE_INPUT_ID)
.font(MONO_FONT)
.size(16)
.padding(Padding {
top: 6.0,
right: 0.0,
bottom: 6.0,
left: 0.0,
})
.align_x(alignment::Horizontal::Center)
.width(Length::Fill)
.style(input_borderless_style);
if !self.running {
value_text = value_text
.on_input(Message::MemoryValueChanged)
.on_submit(Message::ApplyMemory);
} else {
value_text = value_text.style(disabled_input_borderless_style);
}
let value_input: Element<'_, Message> = container(value_text)
.width(Length::Fixed(58.0))
.style(move |theme| input_shell_style(theme, value_focused && !self.running))
.into();
let controls = row![
spinner_text_input(
self.input_placeholder(MEMORY_ADDRESS_INPUT_ID, "0000"),
&self.memory_address_input,
Message::MemoryAddressChanged,
Message::MemoryAddressNext,
Message::MemoryAddressPrevious,
Length::Fixed(96.0),
Message::JumpMemoryAddress,
MEMORY_ADDRESS_INPUT_ID,
self.focused_input == Some(MEMORY_ADDRESS_INPUT_ID),
self.running,
),
value_input,
if self.running {
enter_button_disabled()
} else {
enter_button(Message::ApplyMemory)
},
]
.spacing(6)
.align_y(alignment::Vertical::Center);
let content = container(controls)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center);
legend_panel(self.lang.t(Key::MemoryEditorTitle), content, Length::Shrink)
}
fn register_editor_panel(&self) -> Element<'_, Message> {
let value_focused = self.focused_input == Some(REGISTER_VALUE_INPUT_ID);
let mut value_text = text_input(
self.input_placeholder(REGISTER_VALUE_INPUT_ID, "00"),
&self.register_value_input,
)
.id(REGISTER_VALUE_INPUT_ID)
.font(MONO_FONT)
.size(16)
.padding(Padding {
top: 6.0,
right: 0.0,
bottom: 6.0,
left: 0.0,
})
.align_x(alignment::Horizontal::Center)
.width(Length::Fill);
if !self.running {
value_text = value_text
.on_input(Message::RegisterValueChanged)
.on_submit(Message::ApplyRegister);
} else {
value_text = value_text.style(disabled_input_borderless_style);
}
let value_input: Element<'_, Message> = container(value_text.style(input_borderless_style))
.width(Length::Fixed(58.0))
.style(move |theme| input_shell_style(theme, value_focused && !self.running))
.into();
let editor = row![
spinner_text_input(
self.input_placeholder(REGISTER_NAME_INPUT_ID, "A"),
&self.register_name_input,
Message::RegisterNameChanged,
Message::RegisterNext,
Message::RegisterPrevious,
Length::Fixed(62.0),
Message::ApplyRegister,
REGISTER_NAME_INPUT_ID,
self.focused_input == Some(REGISTER_NAME_INPUT_ID),
self.running,
),
value_input,
if self.running {
enter_button_disabled()
} else {
enter_button(Message::ApplyRegister)
},
]
.spacing(6)
.align_y(alignment::Vertical::Center);
let content = container(editor)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center);
legend_panel(
self.lang.t(Key::RegisterEditorTitle),
content,
Length::Shrink,
)
}
fn actions_panel(&self) -> Element<'_, Message> {
const CHIP_SPACING: f32 = 14.0;
let (run_icon, run_accent, run_tooltip) = if self.running {
(icons::pause(), TOKYO_RED, self.lang.t(Key::ActionPause))
} else {
(
icons::play(),
TOKYO_GREEN,
self.lang.t(Key::ActionRunProgram),
)
};
let (step_icon, step_message, step_tooltip) = if self.running {
(
icons::refresh_ccw(),
Message::RestartProgram,
self.lang.t(Key::ActionRestartProgram),
)
} else {
(
icons::step_forward(),
Message::StepInstruction,
self.lang.t(Key::ActionStepInstruction),
)
};
let blocked = self.run_blocked_after_halt;
let gate = |msg: Message| if blocked { None } else { Some(msg) };
let step_shortcut = if self.running {
None
} else {
shortcut_hint(&Message::StepInstruction)
};
let execution_strip = row![
icon_action_button(
run_icon,
gate(Message::ToggleRun),
run_accent,
run_tooltip,
shortcut_hint(&Message::ToggleRun),
),
icon_action_button(
step_icon,
gate(step_message),
TOKYO_BLUE,
step_tooltip,
step_shortcut,
),
icon_action_button(
icons::redo_dot(),
gate(Message::StepTact),
TOKYO_YELLOW,
self.lang.t(Key::ActionStepTact),
shortcut_hint(&Message::StepTact),
),
]
.spacing(CHIP_SPACING)
.align_y(alignment::Vertical::Center);
let reset_strip = row![
icon_action_button(
icons::reset_ram(),
Some(Message::ResetRam),
TOKYO_RED,
self.lang.t(Key::ActionResetRam),
shortcut_hint(&Message::ResetRam),
),
icon_action_button(
icons::reset_registers(),
Some(Message::ResetCpu),
TOKYO_MAGENTA,
self.lang.t(Key::ActionResetCpu),
shortcut_hint(&Message::ResetCpu),
),
]
.spacing(CHIP_SPACING)
.align_y(alignment::Vertical::Center);
let execution_panel = legend_panel(
self.lang.t(Key::ExecutionPanel),
container(execution_strip)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center),
Length::Shrink,
);
let reset_panel = legend_panel(
self.lang.t(Key::ResetPanel),
container(reset_strip)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center),
Length::Shrink,
);
const EXECUTION_PANEL_WIDTH: f32 = 186.0;
const RESET_PANEL_WIDTH: f32 = 134.0;
row![
container(execution_panel).width(Length::Fixed(EXECUTION_PANEL_WIDTH)),
container(reset_panel).width(Length::Fixed(RESET_PANEL_WIDTH)),
]
.spacing(8)
.align_y(alignment::Vertical::Center)
.into()
}
}