refactor: replace standard sliders with PlainSlider for better UI consistency
This commit is contained in:
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use crate::theme::ThemeColors;
|
use crate::theme::ThemeColors;
|
||||||
use iced::widget::{button, column, horizontal_rule, row, slider, text};
|
use crate::widgets::plain_slider::plain_slider;
|
||||||
use iced::{Element, Length};
|
use iced::widget::{column, horizontal_rule, row, text};
|
||||||
|
use iced::Element;
|
||||||
|
|
||||||
/// Build the Brightness/Contrast adjustment dialog.
|
/// Build the Brightness/Contrast adjustment dialog.
|
||||||
pub fn brightness_contrast_view(
|
pub fn brightness_contrast_view(
|
||||||
@@ -11,30 +12,35 @@ pub fn brightness_contrast_view(
|
|||||||
contrast: f32,
|
contrast: f32,
|
||||||
colors: ThemeColors,
|
colors: ThemeColors,
|
||||||
) -> Element<'static, Message> {
|
) -> Element<'static, Message> {
|
||||||
let brightness_slider = slider(-100.0..=100.0, brightness, |v| Message::AdjBrightness(v))
|
let brightness_slider = plain_slider(
|
||||||
.step(1.0)
|
"Brightness",
|
||||||
.width(Length::Fill);
|
brightness,
|
||||||
|
-100.0..=100.0,
|
||||||
|
1.0,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
|v| Message::AdjBrightness(v),
|
||||||
|
);
|
||||||
|
|
||||||
let contrast_slider = slider(-100.0..=100.0, contrast, |v| Message::AdjContrast(v))
|
let contrast_slider = plain_slider(
|
||||||
.step(1.0)
|
"Contrast",
|
||||||
.width(Length::Fill);
|
contrast,
|
||||||
|
-100.0..=100.0,
|
||||||
|
1.0,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
|v| Message::AdjContrast(v),
|
||||||
|
);
|
||||||
|
|
||||||
let apply_btn = button(text("Apply").size(11))
|
let apply_btn = super::primary_btn("Apply", Message::AdjApply, colors);
|
||||||
.on_press(Message::AdjApply)
|
let cancel_btn = super::secondary_btn("Cancel", Message::DialogClose, colors);
|
||||||
.padding([6, 12]);
|
|
||||||
|
|
||||||
let cancel_btn = button(text("Cancel").size(11))
|
|
||||||
.on_press(Message::DialogClose)
|
|
||||||
.padding([6, 12]);
|
|
||||||
|
|
||||||
let dialog = column![
|
let dialog = column![
|
||||||
text("Brightness / Contrast")
|
text("Brightness / Contrast")
|
||||||
.size(14)
|
.size(14)
|
||||||
.font(iced::Font::MONOSPACE),
|
.font(iced::Font::MONOSPACE),
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
text(format!("Brightness: {:.0}", brightness)).size(11),
|
|
||||||
brightness_slider,
|
brightness_slider,
|
||||||
text(format!("Contrast: {:.0}", contrast)).size(11),
|
|
||||||
contrast_slider,
|
contrast_slider,
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
row![apply_btn, cancel_btn].spacing(12),
|
row![apply_btn, cancel_btn].spacing(12),
|
||||||
@@ -53,36 +59,28 @@ pub fn hsl_view(
|
|||||||
lightness: f32,
|
lightness: f32,
|
||||||
colors: ThemeColors,
|
colors: ThemeColors,
|
||||||
) -> Element<'static, Message> {
|
) -> Element<'static, Message> {
|
||||||
let hue_slider = slider(-180.0..=180.0, hue, |v| Message::AdjHue(v))
|
let hue_slider = plain_slider("Hue", hue, -180.0..=180.0, 1.0, "°", 0, |v| {
|
||||||
.step(1.0)
|
Message::AdjHue(v)
|
||||||
.width(Length::Fill);
|
});
|
||||||
|
|
||||||
let sat_slider = slider(-100.0..=100.0, saturation, |v| Message::AdjSaturation(v))
|
let sat_slider = plain_slider("Saturation", saturation, -100.0..=100.0, 1.0, "", 0, |v| {
|
||||||
.step(1.0)
|
Message::AdjSaturation(v)
|
||||||
.width(Length::Fill);
|
});
|
||||||
|
|
||||||
let light_slider = slider(-100.0..=100.0, lightness, |v| Message::AdjLightness(v))
|
let light_slider = plain_slider("Lightness", lightness, -100.0..=100.0, 1.0, "", 0, |v| {
|
||||||
.step(1.0)
|
Message::AdjLightness(v)
|
||||||
.width(Length::Fill);
|
});
|
||||||
|
|
||||||
let apply_btn = button(text("Apply").size(11))
|
let apply_btn = super::primary_btn("Apply", Message::AdjApply, colors);
|
||||||
.on_press(Message::AdjApply)
|
let cancel_btn = super::secondary_btn("Cancel", Message::DialogClose, colors);
|
||||||
.padding([6, 12]);
|
|
||||||
|
|
||||||
let cancel_btn = button(text("Cancel").size(11))
|
|
||||||
.on_press(Message::DialogClose)
|
|
||||||
.padding([6, 12]);
|
|
||||||
|
|
||||||
let dialog = column![
|
let dialog = column![
|
||||||
text("Hue / Saturation / Lightness")
|
text("Hue / Saturation / Lightness")
|
||||||
.size(14)
|
.size(14)
|
||||||
.font(iced::Font::MONOSPACE),
|
.font(iced::Font::MONOSPACE),
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
text(format!("Hue: {:.0}°", hue)).size(11),
|
|
||||||
hue_slider,
|
hue_slider,
|
||||||
text(format!("Saturation: {:.0}", saturation)).size(11),
|
|
||||||
sat_slider,
|
sat_slider,
|
||||||
text(format!("Lightness: {:.0}", lightness)).size(11),
|
|
||||||
light_slider,
|
light_slider,
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
row![apply_btn, cancel_btn].spacing(12),
|
row![apply_btn, cancel_btn].spacing(12),
|
||||||
|
|||||||
@@ -2,24 +2,17 @@
|
|||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use crate::theme::ThemeColors;
|
use crate::theme::ThemeColors;
|
||||||
use iced::widget::{button, column, horizontal_rule, row, slider, text};
|
use crate::widgets::plain_slider::plain_slider;
|
||||||
use iced::{Element, Length};
|
use iced::widget::{column, horizontal_rule, row, text};
|
||||||
|
use iced::Element;
|
||||||
|
|
||||||
/// Build a confirmation dialog with save/discard/cancel.
|
/// Build a confirmation dialog with save/discard/cancel.
|
||||||
pub fn close_confirm_view(doc_name: &str, colors: ThemeColors) -> Element<'static, Message> {
|
pub fn close_confirm_view(doc_name: &str, colors: ThemeColors) -> Element<'static, Message> {
|
||||||
let msg = format!("\"{}\" has unsaved changes.", doc_name);
|
let msg = format!("\"{}\" has unsaved changes.", doc_name);
|
||||||
|
|
||||||
let save_btn = button(text("Save").size(12))
|
let save_btn = super::primary_btn("Save", Message::DialogCloseSave, colors);
|
||||||
.on_press(Message::DialogCloseSave)
|
let discard_btn = super::secondary_btn("Discard", Message::DialogCloseDiscard, colors);
|
||||||
.padding([8, 16]);
|
let cancel_btn = super::secondary_btn("Cancel", Message::DialogClose, colors);
|
||||||
|
|
||||||
let discard_btn = button(text("Discard").size(12))
|
|
||||||
.on_press(Message::DialogCloseDiscard)
|
|
||||||
.padding([8, 16]);
|
|
||||||
|
|
||||||
let cancel_btn = button(text("Cancel").size(12))
|
|
||||||
.on_press(Message::DialogClose)
|
|
||||||
.padding([8, 16]);
|
|
||||||
|
|
||||||
let dialog = column![
|
let dialog = column![
|
||||||
text("Unsaved Changes").size(16).font(iced::Font::MONOSPACE),
|
text("Unsaved Changes").size(16).font(iced::Font::MONOSPACE),
|
||||||
@@ -44,22 +37,16 @@ pub fn selection_op_view(
|
|||||||
colors: ThemeColors,
|
colors: ThemeColors,
|
||||||
) -> Element<'static, Message> {
|
) -> Element<'static, Message> {
|
||||||
let op_owned = op_name.to_string();
|
let op_owned = op_name.to_string();
|
||||||
let value_slider = slider(min..=max, value, |v| Message::SelectionOpValue(v))
|
let value_slider = plain_slider("Value", value, min..=max, 1.0, "", 0, |v| {
|
||||||
.step(1.0)
|
Message::SelectionOpValue(v)
|
||||||
.width(Length::Fill);
|
});
|
||||||
|
|
||||||
let apply_btn = button(text("Apply").size(11))
|
let apply_btn = super::primary_btn("Apply", Message::SelectionOpApply, colors);
|
||||||
.on_press(Message::SelectionOpApply)
|
let cancel_btn = super::secondary_btn("Cancel", Message::DialogClose, colors);
|
||||||
.padding([6, 12]);
|
|
||||||
|
|
||||||
let cancel_btn = button(text("Cancel").size(11))
|
|
||||||
.on_press(Message::DialogClose)
|
|
||||||
.padding([6, 12]);
|
|
||||||
|
|
||||||
let dialog = column![
|
let dialog = column![
|
||||||
text(op_owned).size(14).font(iced::Font::MONOSPACE),
|
text(op_owned).size(14).font(iced::Font::MONOSPACE),
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
text(format!("Value: {:.0}", value)).size(11),
|
|
||||||
value_slider,
|
value_slider,
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
row![apply_btn, cancel_btn].spacing(12),
|
row![apply_btn, cancel_btn].spacing(12),
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use crate::theme::ThemeColors;
|
use crate::theme::ThemeColors;
|
||||||
use iced::widget::container;
|
use iced::widget::{button, container, text};
|
||||||
use iced::{Element, Length};
|
use iced::{Border, Element, Length};
|
||||||
|
|
||||||
pub mod about;
|
pub mod about;
|
||||||
pub mod adjustments;
|
pub mod adjustments;
|
||||||
@@ -55,3 +55,85 @@ pub fn modal<'a>(
|
|||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blend a color with white by a factor (0.0 = no blend, 1.0 = full white).
|
||||||
|
fn lighten(c: iced::Color, factor: f32) -> iced::Color {
|
||||||
|
iced::Color::new(
|
||||||
|
c.r + (1.0 - c.r) * factor,
|
||||||
|
c.g + (1.0 - c.g) * factor,
|
||||||
|
c.b + (1.0 - c.b) * factor,
|
||||||
|
c.a,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blend a color with black by a factor (0.0 = no blend, 1.0 = full black).
|
||||||
|
fn darken(c: iced::Color, factor: f32) -> iced::Color {
|
||||||
|
iced::Color::new(
|
||||||
|
c.r * (1.0 - factor),
|
||||||
|
c.g * (1.0 - factor),
|
||||||
|
c.b * (1.0 - factor),
|
||||||
|
c.a,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modern theme-compatible primary button (accent fill).
|
||||||
|
pub fn primary_btn(
|
||||||
|
label: &str,
|
||||||
|
msg: Message,
|
||||||
|
colors: ThemeColors,
|
||||||
|
) -> Element<'static, Message> {
|
||||||
|
let l = label.to_string();
|
||||||
|
let c = colors;
|
||||||
|
button(text(l).size(12).style(move |_theme: &iced::Theme| iced::widget::text::Style {
|
||||||
|
color: Some(iced::Color::WHITE),
|
||||||
|
}))
|
||||||
|
.on_press(msg)
|
||||||
|
.padding([8, 16])
|
||||||
|
.style(move |_theme, status| {
|
||||||
|
let bg = match status {
|
||||||
|
iced::widget::button::Status::Hovered => lighten(c.accent, 0.15),
|
||||||
|
iced::widget::button::Status::Pressed => darken(c.accent, 0.15),
|
||||||
|
_ => c.accent,
|
||||||
|
};
|
||||||
|
iced::widget::button::Style {
|
||||||
|
background: Some(iced::Background::Color(bg)),
|
||||||
|
text_color: iced::Color::WHITE,
|
||||||
|
border: Border::default().rounded(6),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modern theme-compatible secondary button (bordered ghost).
|
||||||
|
pub fn secondary_btn(
|
||||||
|
label: &str,
|
||||||
|
msg: Message,
|
||||||
|
colors: ThemeColors,
|
||||||
|
) -> Element<'static, Message> {
|
||||||
|
let l = label.to_string();
|
||||||
|
let c = colors;
|
||||||
|
button(text(l).size(12).style(move |_theme: &iced::Theme| iced::widget::text::Style {
|
||||||
|
color: Some(c.text_primary),
|
||||||
|
}))
|
||||||
|
.on_press(msg)
|
||||||
|
.padding([8, 16])
|
||||||
|
.style(move |_theme, status| {
|
||||||
|
let bg = match status {
|
||||||
|
iced::widget::button::Status::Hovered => {
|
||||||
|
let mut h = c.bg_hover;
|
||||||
|
h.a *= 0.5;
|
||||||
|
h
|
||||||
|
}
|
||||||
|
iced::widget::button::Status::Pressed => c.bg_hover,
|
||||||
|
_ => iced::Color::TRANSPARENT,
|
||||||
|
};
|
||||||
|
iced::widget::button::Style {
|
||||||
|
background: Some(iced::Background::Color(bg)),
|
||||||
|
text_color: c.text_primary,
|
||||||
|
border: Border::default().rounded(6).color(c.border_high).width(1),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use crate::theme::ThemeColors;
|
use crate::theme::ThemeColors;
|
||||||
|
use crate::widgets::plain_slider::plain_slider;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, checkbox, column, horizontal_rule, row, scrollable, slider, text, text_input,
|
button, checkbox, column, horizontal_rule, row, scrollable, text, text_input,
|
||||||
};
|
};
|
||||||
use iced::{Element, Length};
|
use iced::{Element, Length};
|
||||||
|
|
||||||
@@ -70,16 +71,25 @@ pub fn view(
|
|||||||
.on_input(|s| Message::DialogNewImageName(s))
|
.on_input(|s| Message::DialogNewImageName(s))
|
||||||
.width(Length::Fill);
|
.width(Length::Fill);
|
||||||
|
|
||||||
let width_text = text(format!("Width: {}", width)).size(11);
|
let width_slider = plain_slider(
|
||||||
let height_text = text(format!("Height: {}", height)).size(11);
|
"Width",
|
||||||
|
width as f32,
|
||||||
|
1.0..=4096.0,
|
||||||
|
1.0,
|
||||||
|
"px",
|
||||||
|
0,
|
||||||
|
|v| Message::DialogNewImageWidth(v as u32),
|
||||||
|
);
|
||||||
|
|
||||||
let width_slider = slider(1_u32..=4096, width, |v| Message::DialogNewImageWidth(v))
|
let height_slider = plain_slider(
|
||||||
.step(1_u32)
|
"Height",
|
||||||
.width(Length::Fill);
|
height as f32,
|
||||||
|
1.0..=4096.0,
|
||||||
let height_slider = slider(1_u32..=4096, height, |v| Message::DialogNewImageHeight(v))
|
1.0,
|
||||||
.step(1_u32)
|
"px",
|
||||||
.width(Length::Fill);
|
0,
|
||||||
|
|v| Message::DialogNewImageHeight(v as u32),
|
||||||
|
);
|
||||||
|
|
||||||
let transparent_check = checkbox("Transparent background", transparent)
|
let transparent_check = checkbox("Transparent background", transparent)
|
||||||
.on_toggle(|v| Message::DialogNewImageTransparent(v));
|
.on_toggle(|v| Message::DialogNewImageTransparent(v));
|
||||||
@@ -96,22 +106,15 @@ pub fn view(
|
|||||||
presets_col = presets_col.push(preset_btn);
|
presets_col = presets_col.push(preset_btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
let create_btn = button(text("Create").size(12))
|
let create_btn = super::primary_btn("Create", Message::DialogNewImageCreate, colors);
|
||||||
.on_press(Message::DialogNewImageCreate)
|
let cancel_btn = super::secondary_btn("Cancel", Message::DialogClose, colors);
|
||||||
.padding([8, 16]);
|
|
||||||
|
|
||||||
let cancel_btn = button(text("Cancel").size(12))
|
|
||||||
.on_press(Message::DialogClose)
|
|
||||||
.padding([8, 16]);
|
|
||||||
|
|
||||||
let dialog = column![
|
let dialog = column![
|
||||||
text("New Image").size(16).font(iced::Font::MONOSPACE),
|
text("New Image").size(16).font(iced::Font::MONOSPACE),
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
text("Name:").size(11),
|
text("Name:").size(11),
|
||||||
name_input,
|
name_input,
|
||||||
width_text,
|
|
||||||
width_slider,
|
width_slider,
|
||||||
height_text,
|
|
||||||
height_slider,
|
height_slider,
|
||||||
transparent_check,
|
transparent_check,
|
||||||
horizontal_rule(1),
|
horizontal_rule(1),
|
||||||
|
|||||||
@@ -10,8 +10,9 @@
|
|||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use crate::theme::ThemeColors;
|
use crate::theme::ThemeColors;
|
||||||
|
use crate::widgets::plain_slider::plain_slider;
|
||||||
use hcie_engine_api::FilterType;
|
use hcie_engine_api::FilterType;
|
||||||
use iced::widget::{column, container, row, slider, text};
|
use iced::widget::{column, container, row, text};
|
||||||
use iced::{Element, Length};
|
use iced::{Element, Length};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
@@ -35,11 +36,11 @@ fn param_str<'a>(params: &'a serde_json::Value, key: &str, default: &'a str) ->
|
|||||||
params.get(key).and_then(|v| v.as_str()).unwrap_or(default)
|
params.get(key).and_then(|v| v.as_str()).unwrap_or(default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a labeled float slider row.
|
/// Build a labeled float slider row with keyboard editing.
|
||||||
///
|
///
|
||||||
/// Returns a row with label, slider, and numeric display. The slider
|
/// Returns a column with label and keyboard-editable slider. The slider
|
||||||
/// emits `Message::FilterParamChanged(filter_id, json!({key: new_value}))`
|
/// emits `Message::FilterParamChanged(filter_id, json!({key: new_value}))`
|
||||||
/// when the user drags it.
|
/// when the user drags or types a value.
|
||||||
fn float_slider(
|
fn float_slider(
|
||||||
label: &str,
|
label: &str,
|
||||||
key: &str,
|
key: &str,
|
||||||
@@ -50,30 +51,28 @@ fn float_slider(
|
|||||||
step: f64,
|
step: f64,
|
||||||
_colors: ThemeColors,
|
_colors: ThemeColors,
|
||||||
) -> Element<'static, Message> {
|
) -> Element<'static, Message> {
|
||||||
let val = value as f32;
|
|
||||||
let min_f = min as f32;
|
|
||||||
let max_f = max as f32;
|
|
||||||
let step_f = step as f32;
|
|
||||||
let key_owned = key.to_string();
|
let key_owned = key.to_string();
|
||||||
let filter_owned = filter_id.to_string();
|
let filter_owned = filter_id.to_string();
|
||||||
let label_owned = label.to_string();
|
let step_f = step as f32;
|
||||||
|
|
||||||
let lbl = text(label_owned).size(11).width(Length::Fixed(80.0));
|
plain_slider(
|
||||||
let display = text(format!("{:.1}", value))
|
label,
|
||||||
.size(11)
|
value as f32,
|
||||||
.width(Length::Fixed(40.0));
|
(min as f32)..=(max as f32),
|
||||||
|
step_f,
|
||||||
let s = slider(min_f..=max_f, val, move |v| {
|
"",
|
||||||
let rounded = (v as f64 / step).round() * step;
|
1,
|
||||||
Message::FilterParamChanged(filter_owned.clone(), json!({ key_owned.clone(): rounded }))
|
move |v| {
|
||||||
})
|
let rounded = (v as f64 / step).round() * step;
|
||||||
.step(step_f)
|
Message::FilterParamChanged(
|
||||||
.width(Length::Fill);
|
filter_owned.clone(),
|
||||||
|
json!({ key_owned.clone(): rounded }),
|
||||||
row![lbl, s, display].spacing(4).into()
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a labeled integer slider row.
|
/// Build a labeled integer slider row with keyboard editing.
|
||||||
fn int_slider(
|
fn int_slider(
|
||||||
label: &str,
|
label: &str,
|
||||||
key: &str,
|
key: &str,
|
||||||
@@ -83,28 +82,23 @@ fn int_slider(
|
|||||||
max: u64,
|
max: u64,
|
||||||
_colors: ThemeColors,
|
_colors: ThemeColors,
|
||||||
) -> Element<'static, Message> {
|
) -> Element<'static, Message> {
|
||||||
let val = value as f32;
|
|
||||||
let min_f = min as f32;
|
|
||||||
let max_f = max as f32;
|
|
||||||
let key_owned = key.to_string();
|
let key_owned = key.to_string();
|
||||||
let filter_owned = filter_id.to_string();
|
let filter_owned = filter_id.to_string();
|
||||||
let label_owned = label.to_string();
|
|
||||||
|
|
||||||
let lbl = text(label_owned).size(11).width(Length::Fixed(80.0));
|
plain_slider(
|
||||||
let display = text(format!("{}", value))
|
label,
|
||||||
.size(11)
|
value as f32,
|
||||||
.width(Length::Fixed(40.0));
|
(min as f32)..=(max as f32),
|
||||||
|
1.0,
|
||||||
let s = slider(min_f..=max_f, val, move |v| {
|
"",
|
||||||
Message::FilterParamChanged(
|
0,
|
||||||
filter_owned.clone(),
|
move |v| {
|
||||||
json!({ key_owned.clone(): v.round() as u64 }),
|
Message::FilterParamChanged(
|
||||||
)
|
filter_owned.clone(),
|
||||||
})
|
json!({ key_owned.clone(): v.round() as u64 }),
|
||||||
.step(1.0)
|
)
|
||||||
.width(Length::Fill);
|
},
|
||||||
|
)
|
||||||
row![lbl, s, display].spacing(4).into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a section header for a group of parameters.
|
/// Build a section header for a group of parameters.
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ use crate::panels::styles;
|
|||||||
use crate::panels::thumbnails;
|
use crate::panels::thumbnails;
|
||||||
use crate::panels::typography::BODY;
|
use crate::panels::typography::BODY;
|
||||||
use crate::theme::ThemeColors;
|
use crate::theme::ThemeColors;
|
||||||
|
use crate::widgets::plain_slider::plain_slider;
|
||||||
use hcie_engine_api::{BlendMode, LayerInfo, LayerType};
|
use hcie_engine_api::{BlendMode, LayerInfo, LayerType};
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, column, container, horizontal_rule, pick_list, row, scrollable, slider, text, Space,
|
button, column, container, horizontal_rule, pick_list, row, scrollable, text, Space,
|
||||||
};
|
};
|
||||||
use iced::{Element, Length};
|
use iced::{Element, Length};
|
||||||
|
|
||||||
@@ -129,10 +130,10 @@ fn layer_thumbnail<'a>(
|
|||||||
colors: ThemeColors,
|
colors: ThemeColors,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
if entry.is_group {
|
if entry.is_group {
|
||||||
return container(text("\u{1F4C1}").size(13))
|
return container(text("\u{1F4C1}").size(16))
|
||||||
.width(28)
|
.width(50)
|
||||||
.height(20)
|
.height(38)
|
||||||
.center_y(Length::Fixed(20.0))
|
.center_y(Length::Fixed(38.0))
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,15 +141,15 @@ fn layer_thumbnail<'a>(
|
|||||||
let thumb = engine
|
let thumb = engine
|
||||||
.get_layer_pixels(info.id)
|
.get_layer_pixels(info.id)
|
||||||
.filter(|px| !px.is_empty() && info.width > 0 && info.height > 0)
|
.filter(|px| !px.is_empty() && info.width > 0 && info.height > 0)
|
||||||
.map(|px| thumbnails::generate_thumbnail(&px, info.width, info.height, 28));
|
.map(|px| thumbnails::generate_thumbnail(&px, info.width, info.height, 48));
|
||||||
|
|
||||||
if let Some(pixels) = thumb {
|
if let Some(pixels) = thumb {
|
||||||
let handle = iced::widget::image::Handle::from_rgba(28, 20, pixels);
|
let handle = iced::widget::image::Handle::from_rgba(48, 36, pixels);
|
||||||
let img = iced::widget::Image::new(handle).width(28).height(20);
|
let img = iced::widget::Image::new(handle).width(48).height(36);
|
||||||
return container(img)
|
return container(img)
|
||||||
.width(30)
|
.width(50)
|
||||||
.height(22)
|
.height(38)
|
||||||
.center_y(Length::Fixed(22.0))
|
.center_y(Length::Fixed(38.0))
|
||||||
.padding(1)
|
.padding(1)
|
||||||
.style(move |_theme| iced::widget::container::Style {
|
.style(move |_theme| iced::widget::container::Style {
|
||||||
border: iced::Border::default().color(colors.border_high).width(1),
|
border: iced::Border::default().color(colors.border_high).width(1),
|
||||||
@@ -165,15 +166,15 @@ fn layer_thumbnail<'a>(
|
|||||||
_ => ("\u{25A3}", colors.text_secondary),
|
_ => ("\u{25A3}", colors.text_secondary),
|
||||||
};
|
};
|
||||||
|
|
||||||
container(text(icon_char).size(10).style(move |_theme: &iced::Theme| {
|
container(text(icon_char).size(12).style(move |_theme: &iced::Theme| {
|
||||||
iced::widget::text::Style {
|
iced::widget::text::Style {
|
||||||
color: Some(icon_color),
|
color: Some(icon_color),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.width(30)
|
.width(50)
|
||||||
.height(22)
|
.height(38)
|
||||||
.center_x(Length::Fixed(30.0))
|
.center_x(Length::Fixed(50.0))
|
||||||
.center_y(Length::Fixed(22.0))
|
.center_y(Length::Fixed(38.0))
|
||||||
.style(move |_theme| iced::widget::container::Style {
|
.style(move |_theme| iced::widget::container::Style {
|
||||||
background: Some(iced::Background::Color(iced::Color::from_rgba(
|
background: Some(iced::Background::Color(iced::Color::from_rgba(
|
||||||
1.0, 1.0, 1.0, 0.05,
|
1.0, 1.0, 1.0, 0.05,
|
||||||
@@ -222,18 +223,17 @@ pub fn view<'a>(
|
|||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.text_size(BODY);
|
.text_size(BODY);
|
||||||
|
|
||||||
let opacity_label = text(format!("{}%", (active_opacity * 100.0) as u32))
|
let opacity_slider = plain_slider(
|
||||||
.size(BODY)
|
"Opacity",
|
||||||
.width(Length::Fixed(32.0));
|
active_opacity,
|
||||||
let opacity_slider = slider(0.0..=1.0, active_opacity, move |v| {
|
0.0..=1.0,
|
||||||
Message::LayerSetOpacity(active_layer_id, v)
|
0.01,
|
||||||
})
|
"%",
|
||||||
.step(0.01)
|
0,
|
||||||
.width(Length::Fill);
|
move |v| Message::LayerSetOpacity(active_layer_id, v),
|
||||||
|
);
|
||||||
|
|
||||||
let opacity_row = row![text("Op").size(BODY), opacity_slider, opacity_label]
|
let opacity_row = opacity_slider;
|
||||||
.spacing(3)
|
|
||||||
.align_y(iced::Alignment::Center);
|
|
||||||
|
|
||||||
let lock_icon = if active_locked {
|
let lock_icon = if active_locked {
|
||||||
"\u{1F512}"
|
"\u{1F512}"
|
||||||
@@ -293,14 +293,14 @@ pub fn view<'a>(
|
|||||||
Space::with_width(Length::Fixed(12.0)).into()
|
Space::with_width(Length::Fixed(12.0)).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Visibility toggle
|
// Visibility toggle (eye icon)
|
||||||
let vis_char = if info.visible { "\u{25CF}" } else { "\u{25CB}" };
|
let vis_char = if info.visible { "\u{1F441}" } else { "\u{1F441}" };
|
||||||
let vis_color = if info.visible {
|
let vis_color = if info.visible {
|
||||||
colors.text_primary
|
colors.text_primary
|
||||||
} else {
|
} else {
|
||||||
colors.text_secondary
|
iced::Color { a: colors.text_secondary.a * 0.4, ..colors.text_secondary }
|
||||||
};
|
};
|
||||||
let vis_btn = button(text(vis_char).size(10).style(move |_theme: &iced::Theme| {
|
let vis_btn = button(text(vis_char).size(11).style(move |_theme: &iced::Theme| {
|
||||||
iced::widget::text::Style {
|
iced::widget::text::Style {
|
||||||
color: Some(vis_color),
|
color: Some(vis_color),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ use crate::app::{Message, ToolState};
|
|||||||
use crate::panels::styles;
|
use crate::panels::styles;
|
||||||
use crate::settings::AppSettings;
|
use crate::settings::AppSettings;
|
||||||
use crate::theme::ThemeColors;
|
use crate::theme::ThemeColors;
|
||||||
|
use crate::widgets::plain_slider::plain_slider;
|
||||||
use hcie_engine_api::Tool;
|
use hcie_engine_api::Tool;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, column, container, horizontal_rule, row, scrollable, slider, text, text_input,
|
button, column, container, horizontal_rule, row, scrollable, text, text_input,
|
||||||
};
|
};
|
||||||
use iced::{Element, Length};
|
use iced::{Element, Length};
|
||||||
|
|
||||||
@@ -462,19 +463,7 @@ fn prop_slider<F>(
|
|||||||
where
|
where
|
||||||
F: Fn(f32) -> Message + 'static,
|
F: Fn(f32) -> Message + 'static,
|
||||||
{
|
{
|
||||||
let l = label.to_string();
|
plain_slider(label, value, min..=max, 0.1, "", 1, msg_fn)
|
||||||
let label_text = text(l).size(10).width(Length::Fixed(70.0));
|
|
||||||
let val_text = text(format!("{:.1}", value))
|
|
||||||
.size(10)
|
|
||||||
.width(Length::Fixed(40.0));
|
|
||||||
let sl = slider(min..=max, value, msg_fn)
|
|
||||||
.step(0.1)
|
|
||||||
.width(Length::Fill);
|
|
||||||
|
|
||||||
row![label_text, sl, val_text]
|
|
||||||
.spacing(4)
|
|
||||||
.align_y(iced::Alignment::Center)
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_row(label: &str, color: [u8; 4], _c: ThemeColors) -> Element<'static, Message> {
|
fn color_row(label: &str, color: [u8; 4], _c: ThemeColors) -> Element<'static, Message> {
|
||||||
|
|||||||
@@ -294,14 +294,19 @@ fn render_shape(layer: &mut Layer, shape: &VectorShape) {
|
|||||||
let (rot_x3, rot_y3) = rotate_point(rx3, ry3, cx, cy, *angle);
|
let (rot_x3, rot_y3) = rotate_point(rx3, ry3, cx, cy, *angle);
|
||||||
let (rot_x4, rot_y4) = rotate_point(rx4, ry4, cx, cy, *angle);
|
let (rot_x4, rot_y4) = rotate_point(rx4, ry4, cx, cy, *angle);
|
||||||
|
|
||||||
draw_line(layer, rot_x1, rot_y1, rot_x2, rot_y2, px_color, *stroke, None);
|
let head_mid_x = (rot_x3 + rot_x4) / 2.0;
|
||||||
|
let head_mid_y = (rot_y3 + rot_y4) / 2.0;
|
||||||
|
|
||||||
|
draw_line(layer, rot_x1, rot_y1, head_mid_x, head_mid_y, px_color, *stroke, None);
|
||||||
draw_line(layer, rot_x2, rot_y2, rot_x3, rot_y3, px_color, *stroke, None);
|
draw_line(layer, rot_x2, rot_y2, rot_x3, rot_y3, px_color, *stroke, None);
|
||||||
draw_line(layer, rot_x2, rot_y2, rot_x4, rot_y4, px_color, *stroke, None);
|
draw_line(layer, rot_x2, rot_y2, rot_x4, rot_y4, px_color, *stroke, None);
|
||||||
|
draw_line(layer, rot_x3, rot_y3, rot_x4, rot_y4, px_color, *stroke, None);
|
||||||
|
|
||||||
if *fill {
|
if *fill {
|
||||||
let fill_alpha = (fill_color[3] as f32 * opacity).round() as u8;
|
let fill_alpha = (fill_color[3] as f32 * opacity).round() as u8;
|
||||||
let px_fill = [fill_color[0], fill_color[1], fill_color[2], fill_alpha];
|
let px_fill = [fill_color[0], fill_color[1], fill_color[2], fill_alpha];
|
||||||
draw_line(layer, rot_x2, rot_y2, rot_x3, rot_y3, px_fill, arrow_head_len, None);
|
let arrowhead_pts = vec![(rot_x2, rot_y2), (rot_x3, rot_y3), (rot_x4, rot_y4)];
|
||||||
|
fill_polygon(layer, &arrowhead_pts, px_fill);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Star { x1, y1, x2, y2, stroke, color, fill_color, fill, points, inner_radius, opacity, angle, .. } => {
|
Star { x1, y1, x2, y2, stroke, color, fill_color, fill, points, inner_radius, opacity, angle, .. } => {
|
||||||
|
|||||||
Reference in New Issue
Block a user