Fix read-only widget appearance and focus, and keep ColorInput picks surviving a layout diff (#4423)

This commit is contained in:
Keavon Chambers
2026-09-15 14:40:13 +02:00
committed by Dennis Kobert
parent ca615fd6fc
commit 35e77de9fe
5 changed files with 36 additions and 10 deletions
@@ -615,7 +615,11 @@ impl ColorPickerMessageHandler {
// Hex // Hex
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("Hex").tooltip_label("Hex Color Code").tooltip_description(HEX_DESCRIPTION).widget_instance(), TextLabel::new("Hex")
.disabled(self.disabled)
.tooltip_label("Hex Color Code")
.tooltip_description(HEX_DESCRIPTION)
.widget_instance(),
Separator::new(SeparatorStyle::Related).widget_instance(), Separator::new(SeparatorStyle::Related).widget_instance(),
TextInput::new(hex_value) TextInput::new(hex_value)
.centered(true) .centered(true)
@@ -628,7 +632,11 @@ impl ColorPickerMessageHandler {
// RGB // RGB
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("RGB").tooltip_label("Red/Green/Blue").tooltip_description("Integers 0–255.").widget_instance(), TextLabel::new("RGB")
.disabled(self.disabled)
.tooltip_label("Red/Green/Blue")
.tooltip_description("Integers 0–255.")
.widget_instance(),
Separator::new(SeparatorStyle::Related).widget_instance(), Separator::new(SeparatorStyle::Related).widget_instance(),
rgb_input(RgbChannel::Red, rgb_255.map(|(r, _, _)| r), "Red Channel", self.disabled), rgb_input(RgbChannel::Red, rgb_255.map(|(r, _, _)| r), "Red Channel", self.disabled),
Separator::new(SeparatorStyle::Related).widget_instance(), Separator::new(SeparatorStyle::Related).widget_instance(),
@@ -640,6 +648,7 @@ impl ColorPickerMessageHandler {
// HSV // HSV
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("HSV") TextLabel::new("HSV")
.disabled(self.disabled)
.tooltip_label("Hue/Saturation/Value") .tooltip_label("Hue/Saturation/Value")
.tooltip_description("Also known as Hue/Saturation/Brightness (HSB), but distinct from Hue/Saturation/Lightness (HSL), a different color model.") .tooltip_description("Also known as Hue/Saturation/Brightness (HSB), but distinct from Hue/Saturation/Lightness (HSL), a different color model.")
.widget_instance(), .widget_instance(),
@@ -677,7 +686,11 @@ impl ColorPickerMessageHandler {
// Alpha // Alpha
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("Alpha").tooltip_label("Alpha").tooltip_description(ALPHA_DESCRIPTION).widget_instance(), TextLabel::new("Alpha")
.disabled(self.disabled)
.tooltip_label("Alpha")
.tooltip_description(ALPHA_DESCRIPTION)
.widget_instance(),
Separator::new(SeparatorStyle::Related).widget_instance(), Separator::new(SeparatorStyle::Related).widget_instance(),
NumberInput::new(if self.is_none { None } else { Some(self.alpha * 100.) }) NumberInput::new(if self.is_none { None } else { Some(self.alpha * 100.) })
.disabled(self.disabled) .disabled(self.disabled)
@@ -698,7 +711,11 @@ impl ColorPickerMessageHandler {
let entries = RadioEntryData::list_from_choice_type(|gradient_spread| ColorPickerMessage::SetGradientSpread { gradient_spread }.into()); let entries = RadioEntryData::list_from_choice_type(|gradient_spread| ColorPickerMessage::SetGradientSpread { gradient_spread }.into());
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("Ends").tooltip_label("Gradient Spread / Cyclic").tooltip_description(ENDS_DESCRIPTION).widget_instance(), TextLabel::new("Ends")
.disabled(self.disabled)
.tooltip_label("Gradient Spread / Cyclic")
.tooltip_description(ENDS_DESCRIPTION)
.widget_instance(),
Separator::new(SeparatorStyle::Related).widget_instance(), Separator::new(SeparatorStyle::Related).widget_instance(),
RadioInput::new(entries) RadioInput::new(entries)
.narrow(true) .narrow(true)
@@ -727,6 +744,7 @@ impl ColorPickerMessageHandler {
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("Intrp.") TextLabel::new("Intrp.")
.disabled(self.disabled)
.tooltip_label("Gradient Interpolation") .tooltip_label("Gradient Interpolation")
.tooltip_description(INTERPOLATION_DESCRIPTION) .tooltip_description(INTERPOLATION_DESCRIPTION)
.widget_instance(), .widget_instance(),
@@ -745,7 +763,11 @@ impl ColorPickerMessageHandler {
let entries = MenuListEntry::sections_from_choice_type(|gradient_space| ColorPickerMessage::SetGradientSpace { gradient_space }.into()); let entries = MenuListEntry::sections_from_choice_type(|gradient_space| ColorPickerMessage::SetGradientSpace { gradient_space }.into());
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("Space").tooltip_label("Gradient Space").tooltip_description(SPACE_DESCRIPTION).widget_instance(), TextLabel::new("Space")
.disabled(self.disabled)
.tooltip_label("Gradient Space")
.tooltip_description(SPACE_DESCRIPTION)
.widget_instance(),
Separator::new(SeparatorStyle::Related).widget_instance(), Separator::new(SeparatorStyle::Related).widget_instance(),
DropdownInput::new(entries) DropdownInput::new(entries)
.selected_index(Some(self.gradient_space as u32)) .selected_index(Some(self.gradient_space as u32))
@@ -762,6 +784,7 @@ impl ColorPickerMessageHandler {
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("Arc") TextLabel::new("Arc")
.disabled(self.disabled)
.tooltip_label("Gradient Hue Direction") .tooltip_label("Gradient Hue Direction")
.tooltip_description(HUE_DIRECTION_DESCRIPTION) .tooltip_description(HUE_DIRECTION_DESCRIPTION)
.widget_instance(), .widget_instance(),
@@ -201,8 +201,12 @@ impl LayoutMessageHandler {
warn!("ColorInput update was not able to be parsed as FillChoice<SRGBA8>: {color_button:?}"); warn!("ColorInput update was not able to be parsed as FillChoice<SRGBA8>: {color_button:?}");
return; return;
}; };
color_button.value = fill_choice; // The stored copy has to keep mirroring what the frontend was last sent, so the rebuilt layout still
(color_button.on_update.callback)(color_button) // diffs against it and syncs picks that leave the swatch unchanged; the callback borrows the new one
let previous_value = std::mem::replace(&mut color_button.value, fill_choice);
let update_message = (color_button.on_update.callback)(color_button);
color_button.value = previous_value;
update_message
} }
}; };
@@ -75,7 +75,7 @@
data-emphasized={emphasized || undefined} data-emphasized={emphasized || undefined}
data-disabled={disabled || undefined} data-disabled={disabled || undefined}
data-text-button data-text-button
tabindex={disabled ? -1 : 0} tabindex={disabled ? -1 : undefined}
data-floating-menu-spawner data-floating-menu-spawner
data-block-hover-transfer={menuListChildrenExists ? undefined : ""} data-block-hover-transfer={menuListChildrenExists ? undefined : ""}
on:click={onClick} on:click={onClick}
@@ -54,7 +54,6 @@
dispatch("checked", inputElement?.checked || false); dispatch("checked", inputElement?.checked || false);
}} }}
{disabled} {disabled}
tabindex={disabled ? -1 : 0}
bind:this={inputElement} bind:this={inputElement}
/> />
<label <label
@@ -134,7 +134,7 @@
{tooltipShortcut} {tooltipShortcut}
on:click={() => !disabled && (open = true)} on:click={() => !disabled && (open = true)}
on:blur={unFocusDropdownBox} on:blur={unFocusDropdownBox}
tabindex={disabled ? -1 : 0} tabindex={disabled ? undefined : 0}
data-floating-menu-spawner data-floating-menu-spawner
> >
{#if activeEntry.icon} {#if activeEntry.icon}