forked from 1b-agent/opencadstudio-upstream
Fix Select Color panel layout found in screenshots
- Give the panel body room for the gray row and info line. - Keep NumberInput digits clear of its arrows and draw the arrows as plain icons in the text colour. - Split the Current / New swatch with a thin divider. - Load the iced_aw icon font in the headless snapshot test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
This commit is contained in:
1 file changed
+113
-29
+113
-29
@@ -13,7 +13,7 @@ use iced::widget::{button, column, container, row, text, text_input, Space};
|
|||||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||||
|
|
||||||
const PANEL_WIDTH: f32 = 400.0;
|
const PANEL_WIDTH: f32 = 400.0;
|
||||||
const BODY_HEIGHT: f32 = 300.0;
|
const BODY_HEIGHT: f32 = 352.0;
|
||||||
const CONTROL_HEIGHT: f32 = 28.0;
|
const CONTROL_HEIGHT: f32 = 28.0;
|
||||||
const SWATCH_WIDTH: f32 = 104.0;
|
const SWATCH_WIDTH: f32 = 104.0;
|
||||||
|
|
||||||
@@ -168,9 +168,7 @@ impl ColorPicker {
|
|||||||
ColorPickerMsg::Hue(h) => self.set_hsb(Hsb { h, ..self.hsb }),
|
ColorPickerMsg::Hue(h) => self.set_hsb(Hsb { h, ..self.hsb }),
|
||||||
ColorPickerMsg::Saturation(s) => self.set_hsb(Hsb { s, ..self.hsb }),
|
ColorPickerMsg::Saturation(s) => self.set_hsb(Hsb { s, ..self.hsb }),
|
||||||
ColorPickerMsg::Brightness(b) => self.set_hsb(Hsb { b, ..self.hsb }),
|
ColorPickerMsg::Brightness(b) => self.set_hsb(Hsb { b, ..self.hsb }),
|
||||||
ColorPickerMsg::SaturationBrightness(s, b) => {
|
ColorPickerMsg::SaturationBrightness(s, b) => self.set_hsb(Hsb { s, b, ..self.hsb }),
|
||||||
self.set_hsb(Hsb { s, b, ..self.hsb })
|
|
||||||
}
|
|
||||||
ColorPickerMsg::Red(red) => {
|
ColorPickerMsg::Red(red) => {
|
||||||
let (_, g, b) = self.rgb();
|
let (_, g, b) = self.rgb();
|
||||||
self.set_rgb(red, g, b);
|
self.set_rgb(red, g, b);
|
||||||
@@ -236,7 +234,9 @@ impl ColorPicker {
|
|||||||
.width(12)
|
.width(12)
|
||||||
.height(12)
|
.height(12)
|
||||||
.style(move |theme: &Theme| swatch_style(theme, rgb_color(sr, sg, sb))),
|
.style(move |theme: &Theme| swatch_style(theme, rgb_color(sr, sg, sb))),
|
||||||
text(math::describe(self.pending)).size(11).font(iced::Font::MONOSPACE),
|
text(math::describe(self.pending))
|
||||||
|
.size(11)
|
||||||
|
.font(iced::Font::MONOSPACE),
|
||||||
]
|
]
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
.align_y(iced::Center);
|
.align_y(iced::Center);
|
||||||
@@ -249,9 +249,7 @@ impl ColorPicker {
|
|||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(1)
|
.height(1)
|
||||||
.style(|theme: &Theme| container::Style {
|
.style(|theme: &Theme| container::Style {
|
||||||
background: Some(Background::Color(
|
background: Some(Background::Color(theme.palette().background.weak.color)),
|
||||||
theme.palette().background.weak.color
|
|
||||||
)),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
info,
|
info,
|
||||||
@@ -372,7 +370,15 @@ impl ColorPicker {
|
|||||||
background: Some(Background::Color(new_bg)),
|
background: Some(Background::Color(new_bg)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
let swatch = container(row![current, new])
|
let divider =
|
||||||
|
container(Space::new())
|
||||||
|
.width(1)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.style(|_theme: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(Color::from_rgba(0.0, 0.0, 0.0, 0.35))),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let swatch = container(row![current, divider, new])
|
||||||
.width(SWATCH_WIDTH)
|
.width(SWATCH_WIDTH)
|
||||||
.height(CONTROL_HEIGHT)
|
.height(CONTROL_HEIGHT)
|
||||||
.padding(1)
|
.padding(1)
|
||||||
@@ -537,10 +543,7 @@ impl ColorPicker {
|
|||||||
fn hsb_sliders(&self) -> Element<'_, Message> {
|
fn hsb_sliders(&self) -> Element<'_, Message> {
|
||||||
let Hsb { h, s, b } = self.hsb;
|
let Hsb { h, s, b } = self.hsb;
|
||||||
let hue_stops = hue_stops();
|
let hue_stops = hue_stops();
|
||||||
let sat_stops = vec![
|
let sat_stops = vec![(0.0, hsb_color(h, 0.0, b)), (1.0, hsb_color(h, 1.0, b))];
|
||||||
(0.0, hsb_color(h, 0.0, b)),
|
|
||||||
(1.0, hsb_color(h, 1.0, b)),
|
|
||||||
];
|
|
||||||
let bright_stops = vec![(0.0, Color::BLACK), (1.0, hsb_color(h, s, 1.0))];
|
let bright_stops = vec![(0.0, Color::BLACK), (1.0, hsb_color(h, s, 1.0))];
|
||||||
column![
|
column![
|
||||||
slider_row(
|
slider_row(
|
||||||
@@ -580,7 +583,10 @@ impl ColorPicker {
|
|||||||
fn rgb_sliders(&self) -> Element<'_, Message> {
|
fn rgb_sliders(&self) -> Element<'_, Message> {
|
||||||
let (r, g, b) = self.rgb();
|
let (r, g, b) = self.rgb();
|
||||||
let ends = |lo: (u8, u8, u8), hi: (u8, u8, u8)| {
|
let ends = |lo: (u8, u8, u8), hi: (u8, u8, u8)| {
|
||||||
vec![(0.0, rgb_color(lo.0, lo.1, lo.2)), (1.0, rgb_color(hi.0, hi.1, hi.2))]
|
vec![
|
||||||
|
(0.0, rgb_color(lo.0, lo.1, lo.2)),
|
||||||
|
(1.0, rgb_color(hi.0, hi.1, hi.2)),
|
||||||
|
]
|
||||||
};
|
};
|
||||||
let channel = |value: u8| f32::from(value) / 255.0;
|
let channel = |value: u8| f32::from(value) / 255.0;
|
||||||
let byte = |v: f32| (v * 255.0).round() as u8;
|
let byte = |v: f32| (v * 255.0).round() as u8;
|
||||||
@@ -591,7 +597,9 @@ impl ColorPicker {
|
|||||||
picker_msg(ColorPickerMsg::Red(byte(v)))
|
picker_msg(ColorPickerMsg::Red(byte(v)))
|
||||||
})
|
})
|
||||||
.wheel_step(1.0 / 255.0),
|
.wheel_step(1.0 / 255.0),
|
||||||
number_box(u16::from(r), 255, |v| picker_msg(ColorPickerMsg::Red(v as u8))),
|
number_box(u16::from(r), 255, |v| picker_msg(ColorPickerMsg::Red(
|
||||||
|
v as u8
|
||||||
|
))),
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
slider_row(
|
slider_row(
|
||||||
@@ -600,7 +608,9 @@ impl ColorPicker {
|
|||||||
picker_msg(ColorPickerMsg::Green(byte(v)))
|
picker_msg(ColorPickerMsg::Green(byte(v)))
|
||||||
})
|
})
|
||||||
.wheel_step(1.0 / 255.0),
|
.wheel_step(1.0 / 255.0),
|
||||||
number_box(u16::from(g), 255, |v| picker_msg(ColorPickerMsg::Green(v as u8))),
|
number_box(u16::from(g), 255, |v| picker_msg(ColorPickerMsg::Green(
|
||||||
|
v as u8
|
||||||
|
))),
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
slider_row(
|
slider_row(
|
||||||
@@ -609,7 +619,9 @@ impl ColorPicker {
|
|||||||
picker_msg(ColorPickerMsg::Blue(byte(v)))
|
picker_msg(ColorPickerMsg::Blue(byte(v)))
|
||||||
})
|
})
|
||||||
.wheel_step(1.0 / 255.0),
|
.wheel_step(1.0 / 255.0),
|
||||||
number_box(u16::from(b), 255, |v| picker_msg(ColorPickerMsg::Blue(v as u8))),
|
number_box(u16::from(b), 255, |v| picker_msg(ColorPickerMsg::Blue(
|
||||||
|
v as u8
|
||||||
|
))),
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -643,9 +655,9 @@ impl ColorPicker {
|
|||||||
let values = row![
|
let values = row![
|
||||||
field(
|
field(
|
||||||
"H",
|
"H",
|
||||||
number_box(h.round() as u16, 360, |v| picker_msg(ColorPickerMsg::Hue(f32::from(
|
number_box(h.round() as u16, 360, |v| picker_msg(ColorPickerMsg::Hue(
|
||||||
v
|
f32::from(v)
|
||||||
)))),
|
))),
|
||||||
"°",
|
"°",
|
||||||
),
|
),
|
||||||
Space::new().width(Length::Fill),
|
Space::new().width(Length::Fill),
|
||||||
@@ -795,7 +807,21 @@ fn number_box(
|
|||||||
.set_size(12.0)
|
.set_size(12.0)
|
||||||
.font(iced::Font::MONOSPACE)
|
.font(iced::Font::MONOSPACE)
|
||||||
.width(Length::Fixed(64.0))
|
.width(Length::Fixed(64.0))
|
||||||
|
// Extra right padding keeps the digits clear of the arrows; padding
|
||||||
|
// below 5 would make NumberInput swap its arrows for + / − buttons.
|
||||||
|
.padding(iced::Padding {
|
||||||
|
top: 5.0,
|
||||||
|
right: 18.0,
|
||||||
|
bottom: 5.0,
|
||||||
|
left: 6.0,
|
||||||
|
})
|
||||||
.align_x(iced::alignment::Horizontal::Right)
|
.align_x(iced::alignment::Horizontal::Right)
|
||||||
|
.style(
|
||||||
|
|theme: &Theme, _status| iced_aw::style::number_input::Style {
|
||||||
|
button_background: None,
|
||||||
|
icon_color: theme.palette().background.base.text.scale_alpha(0.75),
|
||||||
|
},
|
||||||
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,7 +998,14 @@ mod tests {
|
|||||||
p.update(ColorPickerMsg::Hue(120.0));
|
p.update(ColorPickerMsg::Hue(120.0));
|
||||||
assert_eq!(p.pending(), AcadColor::Rgb { r: 0, g: 255, b: 0 });
|
assert_eq!(p.pending(), AcadColor::Rgb { r: 0, g: 255, b: 0 });
|
||||||
p.update(ColorPickerMsg::Red(10));
|
p.update(ColorPickerMsg::Red(10));
|
||||||
assert_eq!(p.pending(), AcadColor::Rgb { r: 10, g: 255, b: 0 });
|
assert_eq!(
|
||||||
|
p.pending(),
|
||||||
|
AcadColor::Rgb {
|
||||||
|
r: 10,
|
||||||
|
g: 255,
|
||||||
|
b: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1027,8 +1060,11 @@ mod tests {
|
|||||||
|
|
||||||
fn simulator(picker: &ColorPicker) -> iced_test::simulator::Simulator<'_, Message> {
|
fn simulator(picker: &ColorPicker) -> iced_test::simulator::Simulator<'_, Message> {
|
||||||
iced_test::simulator::Simulator::with_size(
|
iced_test::simulator::Simulator::with_size(
|
||||||
iced::Settings::default(),
|
iced::Settings {
|
||||||
(PANEL_WIDTH + 24.0, 440.0),
|
fonts: vec![std::borrow::Cow::Borrowed(iced_aw::ICED_AW_FONT_BYTES)],
|
||||||
|
..iced::Settings::default()
|
||||||
|
},
|
||||||
|
(PANEL_WIDTH + 24.0, 560.0),
|
||||||
container(picker.view()).padding(12),
|
container(picker.view()).padding(12),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1076,12 +1112,60 @@ mod tests {
|
|||||||
fn every_view_renders() {
|
fn every_view_renders() {
|
||||||
let folder = std::env::var_os("OCS_COLOR_PICKER_SNAPSHOTS").map(std::path::PathBuf::from);
|
let folder = std::env::var_os("OCS_COLOR_PICKER_SNAPSHOTS").map(std::path::PathBuf::from);
|
||||||
let views: [(&str, AcadColor, ColorPickerTab, TrueColorMode, bool); 6] = [
|
let views: [(&str, AcadColor, ColorPickerTab, TrueColorMode, bool); 6] = [
|
||||||
("index", AcadColor::Index(140), ColorPickerTab::Index, TrueColorMode::Hsb, false),
|
(
|
||||||
("varies", AcadColor::Index(30), ColorPickerTab::Index, TrueColorMode::Hsb, true),
|
"index",
|
||||||
("hsb", AcadColor::Rgb { r: 57, g: 191, b: 252 }, ColorPickerTab::TrueColor, TrueColorMode::Hsb, false),
|
AcadColor::Index(140),
|
||||||
("rgb", AcadColor::Rgb { r: 57, g: 191, b: 252 }, ColorPickerTab::TrueColor, TrueColorMode::Rgb, false),
|
ColorPickerTab::Index,
|
||||||
("gradient", AcadColor::Rgb { r: 57, g: 191, b: 252 }, ColorPickerTab::TrueColor, TrueColorMode::Gradient, false),
|
TrueColorMode::Hsb,
|
||||||
("exact", AcadColor::Rgb { r: 255, g: 0, b: 0 }, ColorPickerTab::TrueColor, TrueColorMode::Hsb, false),
|
false,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"varies",
|
||||||
|
AcadColor::Index(30),
|
||||||
|
ColorPickerTab::Index,
|
||||||
|
TrueColorMode::Hsb,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"hsb",
|
||||||
|
AcadColor::Rgb {
|
||||||
|
r: 57,
|
||||||
|
g: 191,
|
||||||
|
b: 252,
|
||||||
|
},
|
||||||
|
ColorPickerTab::TrueColor,
|
||||||
|
TrueColorMode::Hsb,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"rgb",
|
||||||
|
AcadColor::Rgb {
|
||||||
|
r: 57,
|
||||||
|
g: 191,
|
||||||
|
b: 252,
|
||||||
|
},
|
||||||
|
ColorPickerTab::TrueColor,
|
||||||
|
TrueColorMode::Rgb,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"gradient",
|
||||||
|
AcadColor::Rgb {
|
||||||
|
r: 57,
|
||||||
|
g: 191,
|
||||||
|
b: 252,
|
||||||
|
},
|
||||||
|
ColorPickerTab::TrueColor,
|
||||||
|
TrueColorMode::Gradient,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"exact",
|
||||||
|
AcadColor::Rgb { r: 255, g: 0, b: 0 },
|
||||||
|
ColorPickerTab::TrueColor,
|
||||||
|
TrueColorMode::Hsb,
|
||||||
|
false,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
for theme in [
|
for theme in [
|
||||||
crate::ui::style::fusion_theme::fusion_black(),
|
crate::ui::style::fusion_theme::fusion_black(),
|
||||||
|
|||||||
Reference in new issue
Block a user