mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-05 22:58:12 +08:00
UI refinements for radio buttons and the Snapping/Grid popovers
This commit is contained in:
@@ -264,6 +264,9 @@ pub struct RadioInput {
|
|||||||
// This uses `u32` instead of `usize` since it will be serialized as a normal JS number (replace this with `usize` after switching to a Rust-based GUI)
|
// This uses `u32` instead of `usize` since it will be serialized as a normal JS number (replace this with `usize` after switching to a Rust-based GUI)
|
||||||
#[serde(rename = "selectedIndex")]
|
#[serde(rename = "selectedIndex")]
|
||||||
pub selected_index: Option<u32>,
|
pub selected_index: Option<u32>,
|
||||||
|
|
||||||
|
#[serde(rename = "minWidth")]
|
||||||
|
pub min_width: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)]
|
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)]
|
||||||
|
|||||||
@@ -1168,8 +1168,6 @@ impl DocumentMessageHandler {
|
|||||||
.options_widget(vec![
|
.options_widget(vec![
|
||||||
LayoutGroup::Row {
|
LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
TextLabel::new(SnappingOptions::BoundingBoxes.to_string()).table_align(true).min_width(96).widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
|
||||||
CheckboxInput::new(snapping_state.bounding_box_snapping)
|
CheckboxInput::new(snapping_state.bounding_box_snapping)
|
||||||
.tooltip(SnappingOptions::BoundingBoxes.to_string())
|
.tooltip(SnappingOptions::BoundingBoxes.to_string())
|
||||||
.on_update(move |input: &CheckboxInput| {
|
.on_update(move |input: &CheckboxInput| {
|
||||||
@@ -1181,13 +1179,11 @@ impl DocumentMessageHandler {
|
|||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
TextLabel::new(SnappingOptions::BoundingBoxes.to_string()).widget_holder(),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
LayoutGroup::Row {
|
LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
TextLabel::new(SnappingOptions::Geometry.to_string()).table_align(true).min_width(96).widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
|
||||||
CheckboxInput::new(self.snapping_state.geometry_snapping)
|
CheckboxInput::new(self.snapping_state.geometry_snapping)
|
||||||
.tooltip(SnappingOptions::Geometry.to_string())
|
.tooltip(SnappingOptions::Geometry.to_string())
|
||||||
.on_update(|input: &CheckboxInput| {
|
.on_update(|input: &CheckboxInput| {
|
||||||
@@ -1199,6 +1195,7 @@ impl DocumentMessageHandler {
|
|||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
|
TextLabel::new(SnappingOptions::Geometry.to_string()).widget_holder(),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
|||||||
widgets.push(LayoutGroup::Row {
|
widgets.push(LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
TextLabel::new("Origin").table_align(true).widget_holder(),
|
TextLabel::new("Origin").table_align(true).widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
NumberInput::new(Some(grid.origin.x))
|
NumberInput::new(Some(grid.origin.x))
|
||||||
.label("X")
|
.label("X")
|
||||||
.unit(" px")
|
.unit(" px")
|
||||||
@@ -127,11 +127,12 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
|||||||
widgets.push(LayoutGroup::Row {
|
widgets.push(LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
TextLabel::new("Type").table_align(true).widget_holder(),
|
TextLabel::new("Type").table_align(true).widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
RadioInput::new(vec![
|
RadioInput::new(vec![
|
||||||
RadioEntryData::new("Rectangular").on_update(update_val(grid, |grid, _| grid.grid_type = GridType::RECTANGLE)),
|
RadioEntryData::new("Rectangular").on_update(update_val(grid, |grid, _| grid.grid_type = GridType::RECTANGLE)),
|
||||||
RadioEntryData::new("Isometric").on_update(update_val(grid, |grid, _| grid.grid_type = GridType::ISOMETRIC)),
|
RadioEntryData::new("Isometric").on_update(update_val(grid, |grid, _| grid.grid_type = GridType::ISOMETRIC)),
|
||||||
])
|
])
|
||||||
|
.min_width(200)
|
||||||
.selected_index(Some(if matches!(grid.grid_type, GridType::Rectangle { .. }) { 0 } else { 1 }))
|
.selected_index(Some(if matches!(grid.grid_type, GridType::Rectangle { .. }) { 0 } else { 1 }))
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
],
|
],
|
||||||
@@ -141,7 +142,7 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
|||||||
GridType::Rectangle { spacing } => widgets.push(LayoutGroup::Row {
|
GridType::Rectangle { spacing } => widgets.push(LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
TextLabel::new("Spacing").table_align(true).widget_holder(),
|
TextLabel::new("Spacing").table_align(true).widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
NumberInput::new(Some(spacing.x))
|
NumberInput::new(Some(spacing.x))
|
||||||
.label("X")
|
.label("X")
|
||||||
.unit(" px")
|
.unit(" px")
|
||||||
@@ -162,10 +163,9 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
|||||||
GridType::Isometric { y_axis_spacing, angle_a, angle_b } => {
|
GridType::Isometric { y_axis_spacing, angle_a, angle_b } => {
|
||||||
widgets.push(LayoutGroup::Row {
|
widgets.push(LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
TextLabel::new("Spacing").table_align(true).widget_holder(),
|
TextLabel::new("Y Spacing").table_align(true).widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
NumberInput::new(Some(y_axis_spacing))
|
NumberInput::new(Some(y_axis_spacing))
|
||||||
.label("Y")
|
|
||||||
.unit(" px")
|
.unit(" px")
|
||||||
.min(0.)
|
.min(0.)
|
||||||
.min_width(200)
|
.min_width(200)
|
||||||
@@ -175,22 +175,17 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
|||||||
});
|
});
|
||||||
widgets.push(LayoutGroup::Row {
|
widgets.push(LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
TextLabel::new("Angle A").table_align(true).widget_holder(),
|
TextLabel::new("Angles").table_align(true).widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
NumberInput::new(Some(angle_a))
|
NumberInput::new(Some(angle_a))
|
||||||
.unit("°")
|
.unit("°")
|
||||||
.min_width(200)
|
.min_width(98)
|
||||||
.on_update(update_origin(&grid, |grid| grid.grid_type.angle_a()))
|
.on_update(update_origin(&grid, |grid| grid.grid_type.angle_a()))
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
],
|
|
||||||
});
|
|
||||||
widgets.push(LayoutGroup::Row {
|
|
||||||
widgets: vec![
|
|
||||||
TextLabel::new("Angle B").table_align(true).widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
NumberInput::new(Some(angle_b))
|
NumberInput::new(Some(angle_b))
|
||||||
.unit("°")
|
.unit("°")
|
||||||
.min_width(200)
|
.min_width(98)
|
||||||
.on_update(update_origin(&grid, |grid| grid.grid_type.angle_b()))
|
.on_update(update_origin(&grid, |grid| grid.grid_type.angle_b()))
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -487,6 +487,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
|||||||
|
|
||||||
responses.add(DocumentMessage::RenderRulers);
|
responses.add(DocumentMessage::RenderRulers);
|
||||||
responses.add(MenuBarMessage::SendLayout);
|
responses.add(MenuBarMessage::SendLayout);
|
||||||
|
responses.add(FrontendMessage::TriggerRefreshBoundsOfViewports);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::UpdateDocumentWidgets => {
|
PortfolioMessage::UpdateDocumentWidgets => {
|
||||||
|
|||||||
@@ -111,6 +111,7 @@
|
|||||||
padding: 0 7px;
|
padding: 0 7px;
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
|
background: var(--color-3-darkgray);
|
||||||
border: 1px solid var(--color-2-mildblack);
|
border: 1px solid var(--color-2-mildblack);
|
||||||
border-radius: 0 0 4px 4px;
|
border-radius: 0 0 4px 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -105,5 +105,9 @@
|
|||||||
background: var(--color-8-uppergray);
|
background: var(--color-8-uppergray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ .text-label.text-label {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
export let entries: RadioEntries;
|
export let entries: RadioEntries;
|
||||||
export let selectedIndex: number | undefined = undefined;
|
export let selectedIndex: number | undefined = undefined;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
|
export let minWidth = 0;
|
||||||
|
|
||||||
$: mixed = selectedIndex === undefined && !disabled;
|
$: mixed = selectedIndex === undefined && !disabled;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LayoutRow class="radio-input" classes={{ disabled }}>
|
<LayoutRow class="radio-input" classes={{ disabled }} styles={{ "min-width": minWidth > 0 ? `${minWidth}px` : "" }}>
|
||||||
{#each entries as entry, index}
|
{#each entries as entry, index}
|
||||||
<button class:active={index === selectedIndex} class:mixed class:disabled on:click={() => handleEntryClick(entry)} title={entry.tooltip} tabindex={index === selectedIndex ? -1 : 0} {disabled}>
|
<button class:active={index === selectedIndex} class:mixed class:disabled on:click={() => handleEntryClick(entry)} title={entry.tooltip} tabindex={index === selectedIndex ? -1 : 0} {disabled}>
|
||||||
{#if entry.icon}
|
{#if entry.icon}
|
||||||
@@ -43,11 +44,15 @@
|
|||||||
fill: var(--color-e-nearwhite);
|
fill: var(--color-e-nearwhite);
|
||||||
height: 24px;
|
height: 24px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 4px;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
// `min-width: fit-content` and `flex: 1 1 0` together allow us to occupy space such that we're always at least the content width,
|
||||||
|
// but if the container is set wider, we distribute the space evenly (so buttons with short and long labels would have equal widths).
|
||||||
|
min-width: fit-content;
|
||||||
|
flex: 1 1 0;
|
||||||
|
|
||||||
&.mixed {
|
&.mixed {
|
||||||
background: var(--color-4-dimgray);
|
background: var(--color-4-dimgray);
|
||||||
@@ -100,11 +105,20 @@
|
|||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
border-radius: 0 2px 2px 0;
|
border-radius: 0 2px 2px 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.text-label {
|
.icon-label {
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
overflow: hidden;
|
|
||||||
|
+ .text-label {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label {
|
||||||
|
margin: 0 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.combined-before button:first-of-type,
|
&.combined-before button:first-of-type,
|
||||||
|
|||||||
@@ -891,6 +891,8 @@ export class RadioInput extends WidgetProps {
|
|||||||
disabled!: boolean;
|
disabled!: boolean;
|
||||||
|
|
||||||
selectedIndex!: number | undefined;
|
selectedIndex!: number | undefined;
|
||||||
|
|
||||||
|
minWidth!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SeparatorDirection = "Horizontal" | "Vertical";
|
export type SeparatorDirection = "Horizontal" | "Vertical";
|
||||||
|
|||||||
Reference in New Issue
Block a user