Update UI widget designs to improve color consistency and add a narrow height mode
|
|
@ -79,18 +79,17 @@ impl LayoutMessageHandler {
|
|||
LayoutGroup::Section { layout, .. } => {
|
||||
stack.extend(layout.iter().enumerate().map(|(index, val)| ([widget_path.as_slice(), &[index]].concat(), val)));
|
||||
}
|
||||
|
||||
LayoutGroup::Table { rows } => {
|
||||
for (row_index, cell) in rows.iter().enumerate() {
|
||||
for (cell_index, entry) in cell.iter().enumerate() {
|
||||
for (row_index, row) in rows.iter().enumerate() {
|
||||
for (cell_index, cell) in row.iter().enumerate() {
|
||||
// Return if this is the correct ID
|
||||
if entry.widget_id == widget_id {
|
||||
if cell.widget_id == widget_id {
|
||||
widget_path.push(row_index);
|
||||
widget_path.push(cell_index);
|
||||
return Some((entry, widget_path));
|
||||
return Some((cell, widget_path));
|
||||
}
|
||||
|
||||
if let Widget::PopoverButton(popover) = &entry.widget {
|
||||
if let Widget::PopoverButton(popover) = &cell.widget {
|
||||
stack.extend(
|
||||
popover
|
||||
.popover_layout
|
||||
|
|
|
|||
|
|
@ -118,6 +118,8 @@ pub struct TextButton {
|
|||
|
||||
pub disabled: bool,
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
|
||||
#[serde(skip)]
|
||||
|
|
@ -179,6 +181,8 @@ pub struct ColorInput {
|
|||
|
||||
pub disabled: bool,
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
#[serde(rename = "menuDirection")]
|
||||
pub menu_direction: Option<MenuDirection>,
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ pub struct DropdownInput {
|
|||
|
||||
pub disabled: bool,
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
|
||||
#[serde(skip)]
|
||||
|
|
@ -195,6 +197,8 @@ pub struct NumberInput {
|
|||
// Disabled
|
||||
pub disabled: bool,
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
// Value
|
||||
#[widget_builder(constructor)]
|
||||
pub value: Option<f64>,
|
||||
|
|
@ -333,6 +337,8 @@ pub struct RadioInput {
|
|||
|
||||
pub disabled: bool,
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
// 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")]
|
||||
pub selected_index: Option<u32>,
|
||||
|
|
@ -409,6 +415,8 @@ pub struct TextInput {
|
|||
|
||||
pub disabled: bool,
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
pub tooltip: String,
|
||||
|
||||
pub centered: bool,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ pub enum SeparatorType {
|
|||
pub struct TextLabel {
|
||||
pub disabled: bool,
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
pub bold: bool,
|
||||
|
||||
pub italic: bool,
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ trait TableRowLayout {
|
|||
fn element_widget(&self, index: usize) -> WidgetHolder {
|
||||
TextButton::new(self.identifier())
|
||||
.on_update(move |_| DataPanelMessage::PushToElementPath { index }.into())
|
||||
.narrow(true)
|
||||
.widget_holder()
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
|
|
@ -227,11 +228,13 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
|
|||
.enumerate()
|
||||
.map(|(index, row)| {
|
||||
vec![
|
||||
TextLabel::new(format!("{index}")).widget_holder(),
|
||||
TextLabel::new(format!("{index}")).narrow(true).widget_holder(),
|
||||
row.element.element_widget(index),
|
||||
TextLabel::new(format_transform_matrix(row.transform)).widget_holder(),
|
||||
TextLabel::new(format!("{}", row.alpha_blending)).widget_holder(),
|
||||
TextLabel::new(row.source_node_id.map_or_else(|| "-".to_string(), |id| format!("{}", id.0))).widget_holder(),
|
||||
TextLabel::new(format_transform_matrix(row.transform)).narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{}", row.alpha_blending)).narrow(true).widget_holder(),
|
||||
TextLabel::new(row.source_node_id.map_or_else(|| "-".to_string(), |id| format!("{}", id.0)))
|
||||
.narrow(true)
|
||||
.widget_holder(),
|
||||
]
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
|
@ -315,104 +318,130 @@ impl TableRowLayout for Vector {
|
|||
|
||||
match self.style.fill.clone() {
|
||||
Fill::None => table_rows.push(vec![
|
||||
TextLabel::new("Fill").widget_holder(),
|
||||
ColorInput::new(FillChoice::None).disabled(true).menu_direction(Some(MenuDirection::Top)).widget_holder(),
|
||||
TextLabel::new("Fill").narrow(true).widget_holder(),
|
||||
ColorInput::new(FillChoice::None).disabled(true).menu_direction(Some(MenuDirection::Top)).narrow(true).widget_holder(),
|
||||
]),
|
||||
Fill::Solid(color) => table_rows.push(vec![
|
||||
TextLabel::new("Fill").widget_holder(),
|
||||
ColorInput::new(FillChoice::Solid(color)).disabled(true).menu_direction(Some(MenuDirection::Top)).widget_holder(),
|
||||
TextLabel::new("Fill").narrow(true).widget_holder(),
|
||||
ColorInput::new(FillChoice::Solid(color))
|
||||
.disabled(true)
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.narrow(true)
|
||||
.widget_holder(),
|
||||
]),
|
||||
Fill::Gradient(gradient) => {
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Fill").widget_holder(),
|
||||
TextLabel::new("Fill").narrow(true).widget_holder(),
|
||||
ColorInput::new(FillChoice::Gradient(gradient.stops))
|
||||
.disabled(true)
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.narrow(true)
|
||||
.widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Fill Gradient Type").widget_holder(),
|
||||
TextLabel::new(gradient.gradient_type.to_string()).widget_holder(),
|
||||
TextLabel::new("Fill Gradient Type").narrow(true).widget_holder(),
|
||||
TextLabel::new(gradient.gradient_type.to_string()).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Fill Gradient Start").widget_holder(),
|
||||
TextLabel::new(format_dvec2(gradient.start)).widget_holder(),
|
||||
TextLabel::new("Fill Gradient Start").narrow(true).widget_holder(),
|
||||
TextLabel::new(format_dvec2(gradient.start)).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Fill Gradient End").narrow(true).widget_holder(),
|
||||
TextLabel::new(format_dvec2(gradient.end)).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![TextLabel::new("Fill Gradient End").widget_holder(), TextLabel::new(format_dvec2(gradient.end)).widget_holder()]);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(stroke) = self.style.stroke.clone() {
|
||||
let color = if let Some(color) = stroke.color { FillChoice::Solid(color) } else { FillChoice::None };
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke").widget_holder(),
|
||||
ColorInput::new(color).disabled(true).menu_direction(Some(MenuDirection::Top)).widget_holder(),
|
||||
TextLabel::new("Stroke").narrow(true).widget_holder(),
|
||||
ColorInput::new(color).disabled(true).menu_direction(Some(MenuDirection::Top)).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![TextLabel::new("Stroke Weight").widget_holder(), TextLabel::new(format!("{} px", stroke.weight)).widget_holder()]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Dash Lengths").widget_holder(),
|
||||
TextLabel::new("Stroke Weight").narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{} px", stroke.weight)).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Dash Lengths").narrow(true).widget_holder(),
|
||||
TextLabel::new(if stroke.dash_lengths.is_empty() {
|
||||
"-".to_string()
|
||||
} else {
|
||||
format!("[{}]", stroke.dash_lengths.iter().map(|x| format!("{x} px")).collect::<Vec<_>>().join(", "))
|
||||
})
|
||||
.narrow(true)
|
||||
.widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Dash Offset").widget_holder(),
|
||||
TextLabel::new(format!("{}", stroke.dash_offset)).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![TextLabel::new("Stroke Cap").widget_holder(), TextLabel::new(stroke.cap.to_string()).widget_holder()]);
|
||||
table_rows.push(vec![TextLabel::new("Stroke Join").widget_holder(), TextLabel::new(stroke.join.to_string()).widget_holder()]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Join Miter Limit").widget_holder(),
|
||||
TextLabel::new(format!("{}", stroke.join_miter_limit)).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![TextLabel::new("Stroke Align").widget_holder(), TextLabel::new(stroke.align.to_string()).widget_holder()]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Transform").widget_holder(),
|
||||
TextLabel::new(format_transform_matrix(&stroke.transform)).widget_holder(),
|
||||
TextLabel::new("Stroke Dash Offset").narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{}", stroke.dash_offset)).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Non-Scaling").widget_holder(),
|
||||
TextLabel::new((if stroke.non_scaling { "Yes" } else { "No" }).to_string()).widget_holder(),
|
||||
TextLabel::new("Stroke Cap").narrow(true).widget_holder(),
|
||||
TextLabel::new(stroke.cap.to_string()).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Paint Order").widget_holder(),
|
||||
TextLabel::new(stroke.paint_order.to_string()).widget_holder(),
|
||||
TextLabel::new("Stroke Join").narrow(true).widget_holder(),
|
||||
TextLabel::new(stroke.join.to_string()).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Join Miter Limit").narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{}", stroke.join_miter_limit)).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Align").narrow(true).widget_holder(),
|
||||
TextLabel::new(stroke.align.to_string()).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Transform").narrow(true).widget_holder(),
|
||||
TextLabel::new(format_transform_matrix(&stroke.transform)).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Non-Scaling").narrow(true).widget_holder(),
|
||||
TextLabel::new((if stroke.non_scaling { "Yes" } else { "No" }).to_string()).narrow(true).widget_holder(),
|
||||
]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Stroke Paint Order").narrow(true).widget_holder(),
|
||||
TextLabel::new(stroke.paint_order.to_string()).narrow(true).widget_holder(),
|
||||
]);
|
||||
}
|
||||
|
||||
let colinear = self.colinear_manipulators.iter().map(|[a, b]| format!("[{a} / {b}]")).collect::<Vec<_>>().join(", ");
|
||||
let colinear = if colinear.is_empty() { "-".to_string() } else { colinear };
|
||||
table_rows.push(vec![TextLabel::new("Colinear Handle IDs").widget_holder(), TextLabel::new(colinear).widget_holder()]);
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Colinear Handle IDs").narrow(true).widget_holder(),
|
||||
TextLabel::new(colinear).narrow(true).widget_holder(),
|
||||
]);
|
||||
|
||||
table_rows.push(vec![
|
||||
TextLabel::new("Upstream Nested Layers").widget_holder(),
|
||||
TextLabel::new("Upstream Nested Layers").narrow(true).widget_holder(),
|
||||
TextLabel::new(if self.upstream_nested_layers.is_some() {
|
||||
"Yes (this preserves references to its upstream nested layers for editing by tools)"
|
||||
} else {
|
||||
"No (this doesn't preserve references to its upstream nested layers for editing by tools)"
|
||||
})
|
||||
.narrow(true)
|
||||
.widget_holder(),
|
||||
]);
|
||||
}
|
||||
VectorTableTab::Points => {
|
||||
table_rows.push(column_headings(&["", "position"]));
|
||||
table_rows.extend(
|
||||
self.point_domain
|
||||
.iter()
|
||||
.map(|(id, position)| vec![TextLabel::new(format!("{}", id.inner())).widget_holder(), TextLabel::new(format!("{position}")).widget_holder()]),
|
||||
);
|
||||
table_rows.extend(self.point_domain.iter().map(|(id, position)| {
|
||||
vec![
|
||||
TextLabel::new(format!("{}", id.inner())).narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{position}")).narrow(true).widget_holder(),
|
||||
]
|
||||
}));
|
||||
}
|
||||
VectorTableTab::Segments => {
|
||||
table_rows.push(column_headings(&["", "start_index", "end_index", "handles"]));
|
||||
table_rows.extend(self.segment_domain.iter().map(|(id, start, end, handles)| {
|
||||
vec![
|
||||
TextLabel::new(format!("{}", id.inner())).widget_holder(),
|
||||
TextLabel::new(format!("{start}")).widget_holder(),
|
||||
TextLabel::new(format!("{end}")).widget_holder(),
|
||||
TextLabel::new(format!("{handles:?}")).widget_holder(),
|
||||
TextLabel::new(format!("{}", id.inner())).narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{start}")).narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{end}")).narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{handles:?}")).narrow(true).widget_holder(),
|
||||
]
|
||||
}));
|
||||
}
|
||||
|
|
@ -420,9 +449,9 @@ impl TableRowLayout for Vector {
|
|||
table_rows.push(column_headings(&["", "segment_range", "fill"]));
|
||||
table_rows.extend(self.region_domain.iter().map(|(id, segment_range, fill)| {
|
||||
vec![
|
||||
TextLabel::new(format!("{}", id.inner())).widget_holder(),
|
||||
TextLabel::new(format!("{segment_range:?}")).widget_holder(),
|
||||
TextLabel::new(format!("{}", fill.inner())).widget_holder(),
|
||||
TextLabel::new(format!("{}", id.inner())).narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{segment_range:?}")).narrow(true).widget_holder(),
|
||||
TextLabel::new(format!("{}", fill.inner())).narrow(true).widget_holder(),
|
||||
]
|
||||
}));
|
||||
}
|
||||
|
|
@ -477,7 +506,11 @@ impl TableRowLayout for Color {
|
|||
format!("Color (#{})", self.to_gamma_srgb().to_rgba_hex_srgb())
|
||||
}
|
||||
fn element_widget(&self, _index: usize) -> WidgetHolder {
|
||||
ColorInput::new(FillChoice::Solid(*self)).disabled(true).menu_direction(Some(MenuDirection::Top)).widget_holder()
|
||||
ColorInput::new(FillChoice::Solid(*self))
|
||||
.disabled(true)
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.narrow(true)
|
||||
.widget_holder()
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![self.element_widget(0)];
|
||||
|
|
@ -494,8 +527,9 @@ impl TableRowLayout for GradientStops {
|
|||
}
|
||||
fn element_widget(&self, _index: usize) -> WidgetHolder {
|
||||
ColorInput::new(FillChoice::Gradient(self.clone()))
|
||||
.disabled(true)
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.disabled(true)
|
||||
.narrow(true)
|
||||
.widget_holder()
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use graphene_std::raster::BlendMode;
|
|||
use graphene_std::raster::Image;
|
||||
use graphene_std::transform::Footprint;
|
||||
use graphene_std::vector::click_target::ClickTarget;
|
||||
use graphene_std::vector::style::ViewMode;
|
||||
use graphene_std::vector::style::RenderMode;
|
||||
|
||||
#[impl_message(Message, PortfolioMessage, Document)]
|
||||
#[derive(derivative::Derivative, Clone, serde::Serialize, serde::Deserialize)]
|
||||
|
|
@ -176,8 +176,8 @@ pub enum DocumentMessage {
|
|||
node_id: NodeId,
|
||||
is_layer: bool,
|
||||
},
|
||||
SetViewMode {
|
||||
view_mode: ViewMode,
|
||||
SetRenderMode {
|
||||
render_mode: RenderMode,
|
||||
},
|
||||
AddTransaction,
|
||||
StartTransaction,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ use graphene_std::table::Table;
|
|||
use graphene_std::vector::PointId;
|
||||
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
||||
use graphene_std::vector::misc::{dvec2_to_point, point_to_dvec2};
|
||||
use graphene_std::vector::style::ViewMode;
|
||||
use graphene_std::vector::style::RenderMode;
|
||||
use kurbo::{Affine, CubicBez, Line, ParamCurve, PathSeg, QuadBez};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
|
@ -92,9 +92,10 @@ pub struct DocumentMessageHandler {
|
|||
pub document_ptz: PTZ,
|
||||
/// The current mode that the document is in, which starts out as Design Mode. This choice affects the editing behavior of the tools.
|
||||
pub document_mode: DocumentMode,
|
||||
/// The current view mode that the user has set for rendering the document within the viewport.
|
||||
/// The current mode that the user has set for rendering the document within the viewport.
|
||||
/// This is usually "Normal" but can be set to "Outline" or "Pixels" to see the canvas differently.
|
||||
pub view_mode: ViewMode,
|
||||
#[serde(alias = "view_mode")]
|
||||
pub render_mode: RenderMode,
|
||||
/// Sets whether or not all the viewport overlays should be drawn on top of the artwork.
|
||||
/// This includes tool interaction visualizations (like the transform cage and path anchors/handles), the grid, and more.
|
||||
pub overlays_visibility_settings: OverlaysVisibilitySettings,
|
||||
|
|
@ -163,7 +164,7 @@ impl Default for DocumentMessageHandler {
|
|||
commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(),
|
||||
document_ptz: PTZ::default(),
|
||||
document_mode: DocumentMode::DesignMode,
|
||||
view_mode: ViewMode::default(),
|
||||
render_mode: RenderMode::default(),
|
||||
overlays_visibility_settings: OverlaysVisibilitySettings::default(),
|
||||
rulers_visible: true,
|
||||
graph_view_overlay_open: false,
|
||||
|
|
@ -1266,8 +1267,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||
responses.add(NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer });
|
||||
responses.add(DocumentMessage::EndTransaction);
|
||||
}
|
||||
DocumentMessage::SetViewMode { view_mode } => {
|
||||
self.view_mode = view_mode;
|
||||
DocumentMessage::SetRenderMode { render_mode } => {
|
||||
self.render_mode = render_mode;
|
||||
responses.add_front(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
DocumentMessage::AddTransaction => {
|
||||
|
|
@ -1821,9 +1822,9 @@ impl DocumentMessageHandler {
|
|||
pub document_ptz: PTZ,
|
||||
/// The current mode that the document is in, which starts out as Design Mode. This choice affects the editing behavior of the tools.
|
||||
pub document_mode: DocumentMode,
|
||||
/// The current view mode that the user has set for rendering the document within the viewport.
|
||||
/// The current mode that the user has set for rendering the document within the viewport.
|
||||
/// This is usually "Normal" but can be set to "Outline" or "Pixels" to see the canvas differently.
|
||||
pub view_mode: ViewMode,
|
||||
pub view_mode: RenderMode,
|
||||
/// Sets whether or not all the viewport overlays should be drawn on top of the artwork.
|
||||
/// This includes tool interaction visualizations (like the transform cage and path anchors/handles), the grid, and more.
|
||||
pub overlays_visibility_settings: OverlaysVisibilitySettings,
|
||||
|
|
@ -1841,7 +1842,7 @@ impl DocumentMessageHandler {
|
|||
commit_hash: old_message_handler.commit_hash,
|
||||
document_ptz: old_message_handler.document_ptz,
|
||||
document_mode: old_message_handler.document_mode,
|
||||
view_mode: old_message_handler.view_mode,
|
||||
render_mode: old_message_handler.view_mode,
|
||||
overlays_visibility_settings: old_message_handler.overlays_visibility_settings,
|
||||
rulers_visible: old_message_handler.rulers_visible,
|
||||
graph_view_overlay_open: old_message_handler.graph_view_overlay_open,
|
||||
|
|
@ -2532,28 +2533,30 @@ impl DocumentMessageHandler {
|
|||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::new("normal")
|
||||
.icon("ViewModeNormal")
|
||||
.tooltip("View Mode: Normal")
|
||||
.on_update(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Normal }.into()),
|
||||
RadioEntryData::new("outline")
|
||||
.icon("ViewModeOutline")
|
||||
.tooltip("View Mode: Outline")
|
||||
.on_update(|_| DocumentMessage::SetViewMode { view_mode: ViewMode::Outline }.into()),
|
||||
RadioEntryData::new("pixels")
|
||||
.icon("ViewModePixels")
|
||||
.tooltip("View Mode: Pixels")
|
||||
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(320) }.into()),
|
||||
RadioEntryData::new("Normal")
|
||||
.icon("RenderModeNormal")
|
||||
.tooltip("Render Mode: Normal")
|
||||
.on_update(|_| DocumentMessage::SetRenderMode { render_mode: RenderMode::Normal }.into()),
|
||||
RadioEntryData::new("Outline")
|
||||
.icon("RenderModeOutline")
|
||||
.tooltip("Render Mode: Outline")
|
||||
.on_update(|_| DocumentMessage::SetRenderMode { render_mode: RenderMode::Outline }.into()),
|
||||
// RadioEntryData::new("PixelPreview")
|
||||
// .icon("RenderModePixels")
|
||||
// .tooltip("Render Mode: Pixel Preview")
|
||||
// .on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(320) }.into()),
|
||||
// RadioEntryData::new("SvgPreview")
|
||||
// .icon("RenderModeSvg")
|
||||
// .tooltip("Render Mode: SVG Preview")
|
||||
// .on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(1845) }.into()),
|
||||
])
|
||||
.selected_index(match self.view_mode {
|
||||
ViewMode::Normal => Some(0),
|
||||
_ => Some(1),
|
||||
})
|
||||
.selected_index(Some(self.render_mode as u32))
|
||||
.narrow(true)
|
||||
.widget_holder(),
|
||||
// PopoverButton::new()
|
||||
// .popover_layout(vec![
|
||||
// LayoutGroup::Row {
|
||||
// widgets: vec![TextLabel::new("View Mode").bold(true).widget_holder()],
|
||||
// widgets: vec![TextLabel::new("Render Mode").bold(true).widget_holder()],
|
||||
// },
|
||||
// LayoutGroup::Row {
|
||||
// widgets: vec![TextLabel::new("Coming soon").widget_holder()],
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ impl NodeGraphExecutor {
|
|||
export_format: graphene_std::application_io::ExportFormat::Canvas,
|
||||
#[cfg(not(any(feature = "resvg", feature = "vello")))]
|
||||
export_format: graphene_std::application_io::ExportFormat::Svg,
|
||||
view_mode: document.view_mode,
|
||||
render_mode: document.render_mode,
|
||||
hide_artboards: false,
|
||||
for_export: false,
|
||||
};
|
||||
|
|
@ -198,7 +198,7 @@ impl NodeGraphExecutor {
|
|||
},
|
||||
time: Default::default(),
|
||||
export_format: graphene_std::application_io::ExportFormat::Svg,
|
||||
view_mode: document.view_mode,
|
||||
render_mode: document.render_mode,
|
||||
hide_artboards: export_config.transparent_background,
|
||||
for_export: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
|
||||
<circle cx="5.75" cy="5.75" r="5.75" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 109 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
|
||||
<path d="M5.75,1A4.75,4.75,0,1,0,10.5,5.75,4.755,4.755,0,0,0,5.75,1m0-1A5.75,5.75,0,1,1,0,5.75,5.75,5.75,0,0,1,5.75,0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 192 B |
|
|
@ -0,0 +1,54 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
|
||||
<rect x="5" y="2" width="1" height="1" />
|
||||
<rect x="2" y="3" width="1" height="1" />
|
||||
<rect x="0" y="3" width="1" height="1" />
|
||||
<rect x="5" y="6" width="1" height="1" />
|
||||
<rect x="2" y="7" width="1" height="1" />
|
||||
<rect x="0" y="7" width="1" height="1" />
|
||||
<rect x="2" y="9" width="1" height="1" />
|
||||
<rect x="6" y="3" width="1" height="1" />
|
||||
<rect x="8" y="3" width="1" height="1" />
|
||||
<rect x="10" y="3" width="1" height="1" />
|
||||
<rect x="3" y="4" width="1" height="1" />
|
||||
<rect x="1" y="4" width="1" height="1" />
|
||||
<rect x="6" y="7" width="1" height="1" />
|
||||
<rect x="8" y="7" width="1" height="1" />
|
||||
<rect x="10" y="7" width="1" height="1" />
|
||||
<rect x="6" y="9" width="1" height="1" />
|
||||
<rect x="8" y="9" width="1" height="1" />
|
||||
<rect x="7" y="8" width="1" height="1" />
|
||||
<rect x="9" y="8" width="1" height="1" />
|
||||
<rect x="7" y="10" width="1" height="1" />
|
||||
<rect x="3" y="8" width="1" height="1" />
|
||||
<rect x="1" y="8" width="1" height="1" />
|
||||
<rect x="3" y="10" width="1" height="1" />
|
||||
<rect x="5" y="8" width="1" height="1" />
|
||||
<rect x="5" y="10" width="1" height="1" />
|
||||
<rect x="7" y="2" width="1" height="1" />
|
||||
<rect x="9" y="2" width="1" height="1" />
|
||||
<rect x="8" y="1" width="1" height="1" />
|
||||
<rect x="4" y="3" width="1" height="1" />
|
||||
<rect x="7" y="6" width="1" height="1" />
|
||||
<rect x="9" y="6" width="1" height="1" />
|
||||
<rect x="4" y="7" width="1" height="1" />
|
||||
<rect x="4" y="9" width="1" height="1" />
|
||||
<rect x="6" y="1" width="1" height="1" />
|
||||
<rect x="3" y="2" width="1" height="1" />
|
||||
<rect x="1" y="2" width="1" height="1" />
|
||||
<rect x="3" y="0" width="1" height="1" />
|
||||
<rect x="6" y="5" width="1" height="1" />
|
||||
<rect x="8" y="5" width="1" height="1" />
|
||||
<rect x="10" y="5" width="1" height="1" />
|
||||
<rect x="3" y="6" width="1" height="1" />
|
||||
<rect x="1" y="6" width="1" height="1" />
|
||||
<rect x="5" y="0" width="1" height="1" />
|
||||
<rect x="2" y="1" width="1" height="1" />
|
||||
<rect x="5" y="4" width="1" height="1" />
|
||||
<rect x="2" y="5" width="1" height="1" />
|
||||
<rect x="0" y="5" width="1" height="1" />
|
||||
<rect x="7" y="0" width="1" height="1" />
|
||||
<rect x="4" y="1" width="1" height="1" />
|
||||
<rect x="7" y="4" width="1" height="1" />
|
||||
<rect x="9" y="4" width="1" height="1" />
|
||||
<rect x="4" y="5" width="1" height="1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
|
||||
<path d="M0,11V8.5H1V10H2.5v1z" />
|
||||
<path d="M0,2.5V0H2.5V1H1V2.5z" />
|
||||
<path d="M9.5,11V10H11V8.5h1V11z" />
|
||||
<path d="M11,2.5V1H9.5V0H12V2.5z" />
|
||||
<path d="M5.75,0.75C8.51,0.75 10.75,2.878 10.75,5.5C10.75,8.122 8.51,10.25 5.75,10.25C2.99,10.25 0.75,8.122 0.75,5.5C0.75,2.878 2.99,0.75 5.75,0.75zM2,6L3.333,6L3.5,6.5L3.333,7L2,7L2,8L4,8L4.5,6.5L5,8L6,8L6.571,6.286L7,8L9.5,8L10,6L10,5L8.5,5L8.5,6L9,6L8.75,7L7.75,7L7.5,6L7.833,4L9,4L9,3L6,3L6,5L5.5,6.5L5,5L5,3L2,3L1.5,4.5L2,6zM2.667,4L4,4L4,5L2.667,5L2.5,4.5L2.667,4z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 592 B |
|
|
@ -1,3 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<circle cx="8" cy="8" r="8" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 100 B |
|
|
@ -1,3 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M8,1c3.9,0,7,3.1,7,7s-3.1,7-7,7s-7-3.1-7-7S4.1,1,8,1 M8,0C3.6,0,0,3.6,0,8s3.6,8,8,8s8-3.6,8-8S12.4,0,8,0L8,0z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 192 B |
|
|
@ -1,23 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect x="7" y="1" width="2" height="2" />
|
||||
<rect x="13" y="4" width="2" height="2" />
|
||||
<rect x="13" y="7" width="2" height="2" />
|
||||
<rect x="13" y="10" width="2" height="2" />
|
||||
<rect x="7" y="13" width="2" height="2" />
|
||||
<rect x="10" y="13" width="2" height="2" />
|
||||
<rect x="4" y="13" width="2" height="2" />
|
||||
<rect x="1" y="10" width="2" height="2" />
|
||||
<rect x="1" y="7" width="2" height="2" />
|
||||
<rect x="1" y="4" width="2" height="2" />
|
||||
<rect x="4" y="1" width="2" height="2" />
|
||||
<rect x="10" y="10" width="2" height="2" />
|
||||
<rect x="4" y="10" width="2" height="2" />
|
||||
<rect x="7" y="10" width="2" height="2" />
|
||||
<rect x="10" y="7" width="2" height="2" />
|
||||
<rect x="4" y="7" width="2" height="2" />
|
||||
<rect x="7" y="7" width="2" height="2" />
|
||||
<rect x="10" y="4" width="2" height="2" />
|
||||
<rect x="4" y="4" width="2" height="2" />
|
||||
<rect x="7" y="4" width="2" height="2" />
|
||||
<rect x="10" y="1" width="2" height="2" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 987 B |
|
|
@ -40,6 +40,8 @@
|
|||
export { className as class };
|
||||
export let classes: Record<string, boolean> = {};
|
||||
|
||||
export let narrow = false;
|
||||
|
||||
$: extraClasses = Object.entries(classes)
|
||||
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||
.join(" ");
|
||||
|
|
@ -82,7 +84,7 @@
|
|||
|
||||
<!-- TODO: Refactor this component to use `<svelte:component this={attributesObject} />` to avoid all the separate conditional components -->
|
||||
|
||||
<div class={`widget-span ${className} ${extraClasses}`.trim()} class:row={direction === "row"} class:column={direction === "column"}>
|
||||
<div class={`widget-span ${className} ${extraClasses}`.trim()} class:narrow class:row={direction === "row"} class:column={direction === "column"}>
|
||||
{#each widgets as component, index}
|
||||
{@const checkboxInput = narrowWidgetProps(component.props, "CheckboxInput")}
|
||||
{#if checkboxInput}
|
||||
|
|
@ -202,11 +204,17 @@
|
|||
.widget-span.row {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
min-height: 32px;
|
||||
--row-height: 32px;
|
||||
min-height: var(--row-height);
|
||||
|
||||
&.narrow {
|
||||
--row-height: 24px;
|
||||
}
|
||||
|
||||
> * {
|
||||
--widget-height: 24px;
|
||||
margin: calc((24px - var(--widget-height)) / 2 + 4px) 0;
|
||||
// Vertically center the widget within the row
|
||||
margin: calc((var(--row-height) - var(--widget-height)) / 2) 0;
|
||||
min-height: var(--widget-height);
|
||||
|
||||
&:not(.multiline) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<tr>
|
||||
{#each row as cell}
|
||||
<td>
|
||||
<WidgetSpan widgetData={{ rowWidgets: [cell] }} {layoutTarget} />
|
||||
<WidgetSpan widgetData={{ rowWidgets: [cell] }} {layoutTarget} narrow={true} />
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
export let flush = false;
|
||||
export let minWidth = 0;
|
||||
export let disabled = false;
|
||||
export let narrow = false;
|
||||
export let tooltip: string | undefined = undefined;
|
||||
export let menuListChildren: MenuListEntry[][] | undefined = undefined;
|
||||
|
||||
|
|
@ -54,6 +55,7 @@
|
|||
class:hover-icon={hoverIcon && !disabled}
|
||||
class:emphasized
|
||||
class:disabled
|
||||
class:narrow
|
||||
class:flush
|
||||
style:min-width={minWidth > 0 ? `${minWidth}px` : undefined}
|
||||
title={tooltip}
|
||||
|
|
@ -99,7 +101,7 @@
|
|||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
height: 24px;
|
||||
height: var(--widget-height);
|
||||
margin: 0;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
|
|
@ -107,8 +109,13 @@
|
|||
border-radius: 2px;
|
||||
background: var(--button-background-color);
|
||||
color: var(--button-text-color);
|
||||
--button-background-color: var(--color-5-dullgray);
|
||||
--button-background-color: var(--color-4-dimgray);
|
||||
--button-text-color: var(--color-e-nearwhite);
|
||||
--widget-height: 24px;
|
||||
|
||||
&.narrow.narrow {
|
||||
--widget-height: 20px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.open {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
export let value: FillChoice;
|
||||
export let disabled = false;
|
||||
export let narrow = false;
|
||||
export let allowNone = false;
|
||||
export let menuDirection: MenuDirection = "Bottom";
|
||||
// export let allowTransparency = false; // TODO: Implement
|
||||
|
|
@ -25,7 +26,7 @@
|
|||
$: transparency = value instanceof Gradient ? value.stops.some((stop) => stop.color.alpha < 1) : value.alpha < 1;
|
||||
</script>
|
||||
|
||||
<LayoutCol class="color-button" classes={{ open, disabled, none, transparency, outlined, "direction-top": menuDirection === "Top" }} {tooltip}>
|
||||
<LayoutCol class="color-button" classes={{ open, disabled, narrow, none, transparency, outlined, "direction-top": menuDirection === "Top" }} {tooltip}>
|
||||
<button style:--chosen-gradient={chosenGradient} style:--outline-amount={outlineFactor} on:click={() => (open = true)} tabindex="0" data-floating-menu-spawner>
|
||||
<!-- {#if disabled && value instanceof Color && !value.none}
|
||||
<TextLabel>sRGB</TextLabel>
|
||||
|
|
@ -55,6 +56,10 @@
|
|||
position: relative;
|
||||
min-width: 80px;
|
||||
|
||||
&.narrow.narrow {
|
||||
--widget-height: 20px;
|
||||
}
|
||||
|
||||
> button {
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
export let drawIcon = false;
|
||||
export let interactive = true;
|
||||
export let disabled = false;
|
||||
export let narrow = false;
|
||||
export let tooltip: string | undefined = undefined;
|
||||
export let minWidth = 0;
|
||||
export let maxWidth = 0;
|
||||
|
|
@ -86,7 +87,11 @@
|
|||
|
||||
<LayoutRow
|
||||
class="dropdown-input"
|
||||
styles={{ ...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}), ...(maxWidth > 0 ? { "max-width": `${maxWidth}px` } : {}) }}
|
||||
classes={{ narrow }}
|
||||
styles={{
|
||||
...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}),
|
||||
...(maxWidth > 0 ? { "max-width": `${maxWidth}px` } : {}),
|
||||
}}
|
||||
bind:this={self}
|
||||
data-dropdown-input
|
||||
>
|
||||
|
|
@ -107,12 +112,12 @@
|
|||
</LayoutRow>
|
||||
<MenuList
|
||||
on:naturalWidth={({ detail }) => (minWidth = detail)}
|
||||
{activeEntry}
|
||||
on:activeEntry={({ detail }) => (activeEntry = detail)}
|
||||
on:hoverInEntry={({ detail }) => dispatchHoverInEntry(detail)}
|
||||
on:hoverOutEntry={() => dispatchHoverOutEntry()}
|
||||
{open}
|
||||
on:open={({ detail }) => (open = detail)}
|
||||
{open}
|
||||
{activeEntry}
|
||||
{entries}
|
||||
{drawIcon}
|
||||
{interactive}
|
||||
|
|
@ -125,13 +130,18 @@
|
|||
<style lang="scss" global>
|
||||
.dropdown-input {
|
||||
position: relative;
|
||||
--widget-height: 24px;
|
||||
|
||||
&.narrow.narrow {
|
||||
--widget-height: 20px;
|
||||
}
|
||||
|
||||
.dropdown-box {
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
background: var(--color-1-nearblack);
|
||||
height: 24px;
|
||||
border-radius: 2px;
|
||||
background: var(--color-1-nearblack);
|
||||
height: var(--widget-height);
|
||||
|
||||
.dropdown-label {
|
||||
margin: 0;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
export let label: string | undefined = undefined;
|
||||
export let spellcheck = false;
|
||||
export let disabled = false;
|
||||
export let narrow = false;
|
||||
export let textarea = false;
|
||||
export let tooltip: string | undefined = undefined;
|
||||
export let placeholder: string | undefined = undefined;
|
||||
|
|
@ -73,7 +74,7 @@
|
|||
</script>
|
||||
|
||||
<!-- This is a base component, extended by others like NumberInput and TextInput. It should not be used directly. -->
|
||||
<LayoutRow class={`field-input ${className}`} classes={{ disabled, ...classes }} style={styleName} {styles} {tooltip}>
|
||||
<LayoutRow class={`field-input ${className}`} classes={{ disabled, narrow, ...classes }} style={styleName} {styles} {tooltip}>
|
||||
{#if !textarea}
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -127,9 +128,13 @@
|
|||
background: var(--color-1-nearblack);
|
||||
flex-direction: row-reverse;
|
||||
|
||||
&.narrow.narrow {
|
||||
--widget-height: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
flex: 0 0 auto;
|
||||
line-height: 18px;
|
||||
line-height: calc(var(--widget-height) - 6px);
|
||||
padding: 3px 0;
|
||||
padding-right: 4px;
|
||||
margin-left: 8px;
|
||||
|
|
@ -147,8 +152,8 @@
|
|||
flex: 1 1 100%;
|
||||
width: 0;
|
||||
min-width: 30px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
height: calc(var(--widget-height) - 6px);
|
||||
line-height: calc(var(--widget-height) - 6px);
|
||||
margin: 0 8px;
|
||||
padding: 3px 0;
|
||||
outline: none; // Ok for input/textarea element
|
||||
|
|
@ -189,7 +194,7 @@
|
|||
}
|
||||
|
||||
textarea {
|
||||
min-height: calc(18px * 3);
|
||||
min-height: calc((var(--widget-height) - 6px) * 3);
|
||||
margin: 3px;
|
||||
padding: 0 5px;
|
||||
box-sizing: border-box;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@
|
|||
// Disabled
|
||||
export let disabled = false;
|
||||
|
||||
// Narrow
|
||||
export let narrow = false;
|
||||
|
||||
// Value
|
||||
// When `value` is not provided (i.e. it's `undefined`), a dash is displayed.
|
||||
export let value: number | undefined = undefined; // NOTE: Do not update this directly, do so by calling `updateValue()` instead.
|
||||
|
|
@ -642,6 +645,7 @@
|
|||
<FieldInput
|
||||
class="number-input"
|
||||
classes={{
|
||||
narrow,
|
||||
increment: mode === "Increment",
|
||||
range: mode === "Range",
|
||||
}}
|
||||
|
|
@ -653,6 +657,7 @@
|
|||
on:pointerdown={onDragPointerDown}
|
||||
{label}
|
||||
{disabled}
|
||||
{narrow}
|
||||
{tooltip}
|
||||
{styles}
|
||||
hideContextMenu={true}
|
||||
|
|
@ -709,7 +714,13 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
&.narrow {
|
||||
--widget-height: 20px;
|
||||
}
|
||||
|
||||
&.increment {
|
||||
--arrow-radius: 3px;
|
||||
|
||||
// Widen the label and input margins from the edges by an extra 8px to make room for the increment arrows
|
||||
label {
|
||||
margin-left: 8px;
|
||||
|
|
@ -738,7 +749,7 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
padding: 9px 0;
|
||||
padding: calc(var(--widget-height) / 2 - var(--arrow-radius)) 0;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
background: rgba(var(--color-1-nearblack-rgb), 0.5);
|
||||
|
|
@ -759,7 +770,7 @@
|
|||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 3px 0 3px 3px;
|
||||
border-width: var(--arrow-radius) 0 var(--arrow-radius) var(--arrow-radius);
|
||||
border-color: transparent transparent transparent var(--color-e-nearwhite);
|
||||
}
|
||||
}
|
||||
|
|
@ -775,7 +786,7 @@
|
|||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 3px 3px 3px 0;
|
||||
border-width: var(--arrow-radius) var(--arrow-radius) var(--arrow-radius) 0;
|
||||
border-color: transparent var(--color-e-nearwhite) transparent transparent;
|
||||
}
|
||||
}
|
||||
|
|
@ -845,7 +856,7 @@
|
|||
appearance: none;
|
||||
border-radius: 2px;
|
||||
width: 4px;
|
||||
height: 22px;
|
||||
height: calc(var(--widget-height) - 2px);
|
||||
background: #494949; // Becomes var(--color-5-dullgray) with screen blend mode over var(--color-1-nearblack) background
|
||||
}
|
||||
|
||||
|
|
@ -862,7 +873,7 @@
|
|||
border: none;
|
||||
border-radius: 2px;
|
||||
width: 4px;
|
||||
height: 22px;
|
||||
height: calc(var(--widget-height) - 2px);
|
||||
background: #494949; // Becomes var(--color-5-dullgray) with screen blend mode over var(--color-1-nearblack) background
|
||||
}
|
||||
|
||||
|
|
@ -900,7 +911,7 @@
|
|||
border-radius: 2px;
|
||||
margin-left: -2px;
|
||||
width: 4px;
|
||||
height: 22px;
|
||||
height: calc(var(--widget-height) - 2px);
|
||||
top: 1px;
|
||||
left: calc(var(--progress-factor) * 100%);
|
||||
background: #5b5b5b; // Becomes var(--color-6-lowergray) with screen blend mode over var(--color-1-nearblack) background
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
export let selectedIndex: number | undefined = undefined;
|
||||
export let disabled = false;
|
||||
export let minWidth = 0;
|
||||
export let narrow = false;
|
||||
|
||||
$: mixed = selectedIndex === undefined && !disabled;
|
||||
|
||||
|
|
@ -24,7 +25,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<LayoutRow class="radio-input" classes={{ disabled, mixed }} styles={{ ...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}) }}>
|
||||
<LayoutRow class="radio-input" classes={{ disabled, narrow, mixed }} styles={{ ...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}) }}>
|
||||
{#each entries as entry, index}
|
||||
<button class:active={!mixed ? index === selectedIndex : undefined} on:click={() => handleEntryClick(entry)} title={entry.tooltip} tabindex={index === selectedIndex ? -1 : 0} {disabled}>
|
||||
{#if entry.icon}
|
||||
|
|
@ -39,12 +40,13 @@
|
|||
|
||||
<style lang="scss" global>
|
||||
.radio-input {
|
||||
background: var(--color-5-dullgray);
|
||||
background: var(--color-4-dimgray);
|
||||
border-radius: 2px;
|
||||
height: 24px;
|
||||
--widget-height: 24px;
|
||||
height: var(--widget-height);
|
||||
|
||||
button {
|
||||
background: var(--color-5-dullgray);
|
||||
background: var(--color-4-dimgray);
|
||||
fill: var(--color-e-nearwhite);
|
||||
border-radius: 2px;
|
||||
height: 20px;
|
||||
|
|
@ -100,33 +102,35 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.mixed {
|
||||
background: var(--color-4-dimgray);
|
||||
&.narrow.narrow {
|
||||
--widget-height: 20px;
|
||||
height: var(--widget-height);
|
||||
|
||||
button {
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&.mixed {
|
||||
button:not(:hover),
|
||||
&.disabled button:hover {
|
||||
background: var(--color-5-dullgray);
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: var(--color-4-dimgray);
|
||||
&.disabled button {
|
||||
color: var(--color-8-uppergray);
|
||||
|
||||
button {
|
||||
background: var(--color-4-dimgray);
|
||||
color: var(--color-8-uppergray);
|
||||
svg {
|
||||
fill: var(--color-8-uppergray);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--color-8-uppergray);
|
||||
color: var(--color-2-mildblack);
|
||||
|
||||
svg {
|
||||
fill: var(--color-8-uppergray);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--color-8-uppergray);
|
||||
color: var(--color-2-mildblack);
|
||||
|
||||
svg {
|
||||
fill: var(--color-2-mildblack);
|
||||
}
|
||||
fill: var(--color-2-mildblack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
export let placeholder: string | undefined = undefined;
|
||||
// Disabled
|
||||
export let disabled = false;
|
||||
// Narrow
|
||||
export let narrow = false;
|
||||
// Value
|
||||
export let value: string;
|
||||
// Styling
|
||||
|
|
@ -76,6 +78,7 @@
|
|||
spellcheck={true}
|
||||
{label}
|
||||
{disabled}
|
||||
{narrow}
|
||||
{tooltip}
|
||||
{placeholder}
|
||||
bind:this={self}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
export { styleName as style };
|
||||
export let styles: Record<string, string | number | undefined> = {};
|
||||
export let disabled = false;
|
||||
export let narrow = false;
|
||||
export let bold = false;
|
||||
export let italic = false;
|
||||
export let monospace = false;
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
<label
|
||||
class={`text-label ${className} ${extraClasses}`.trim()}
|
||||
class:disabled
|
||||
class:narrow
|
||||
class:bold
|
||||
class:italic
|
||||
class:monospace
|
||||
|
|
@ -48,6 +50,10 @@
|
|||
// Force Safari to not draw a text cursor, even though this element has `user-select: none`
|
||||
cursor: default;
|
||||
|
||||
&.narrow.narrow {
|
||||
--widget-height: 20px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: var(--color-8-uppergray);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -942,6 +942,8 @@ export class ColorInput extends WidgetProps {
|
|||
|
||||
disabled!: boolean;
|
||||
|
||||
narrow!: boolean;
|
||||
|
||||
menuDirection!: MenuDirection | undefined;
|
||||
|
||||
// allowTransparency!: boolean; // TODO: Implement
|
||||
|
|
@ -1039,6 +1041,8 @@ export class DropdownInput extends WidgetProps {
|
|||
|
||||
disabled!: boolean;
|
||||
|
||||
narrow!: boolean;
|
||||
|
||||
@Transform(({ value }: { value: string }) => value || undefined)
|
||||
tooltip!: string | undefined;
|
||||
|
||||
|
|
@ -1127,6 +1131,9 @@ export class NumberInput extends WidgetProps {
|
|||
|
||||
disabled!: boolean;
|
||||
|
||||
// Narrow
|
||||
narrow!: boolean;
|
||||
|
||||
// Value
|
||||
|
||||
value!: number | undefined;
|
||||
|
|
@ -1204,6 +1211,8 @@ export class RadioInput extends WidgetProps {
|
|||
|
||||
disabled!: boolean;
|
||||
|
||||
narrow!: boolean;
|
||||
|
||||
selectedIndex!: number | undefined;
|
||||
|
||||
minWidth!: number;
|
||||
|
|
@ -1261,6 +1270,8 @@ export class TextButton extends WidgetProps {
|
|||
|
||||
disabled!: boolean;
|
||||
|
||||
narrow!: boolean;
|
||||
|
||||
@Transform(({ value }: { value: string }) => value || undefined)
|
||||
tooltip!: string | undefined;
|
||||
|
||||
|
|
@ -1302,6 +1313,8 @@ export class TextInput extends WidgetProps {
|
|||
|
||||
disabled!: boolean;
|
||||
|
||||
narrow!: boolean;
|
||||
|
||||
minWidth!: number;
|
||||
|
||||
maxWidth!: number;
|
||||
|
|
@ -1317,6 +1330,8 @@ export class TextLabel extends WidgetProps {
|
|||
// Props
|
||||
disabled!: boolean;
|
||||
|
||||
narrow!: boolean;
|
||||
|
||||
bold!: boolean;
|
||||
|
||||
italic!: boolean;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ import License12px from "@graphite-frontend/assets/icon-12px-solid/license-12px.
|
|||
import Link from "@graphite-frontend/assets/icon-12px-solid/link.svg";
|
||||
import Overlays from "@graphite-frontend/assets/icon-12px-solid/overlays.svg";
|
||||
import Remove from "@graphite-frontend/assets/icon-12px-solid/remove.svg";
|
||||
import RenderModeNormal from "@graphite-frontend/assets/icon-12px-solid/render-mode-normal.svg";
|
||||
import RenderModeOutline from "@graphite-frontend/assets/icon-12px-solid/render-mode-outline.svg";
|
||||
import RenderModePixels from "@graphite-frontend/assets/icon-12px-solid/render-mode-pixels.svg";
|
||||
import RenderModeSvg from "@graphite-frontend/assets/icon-12px-solid/render-mode-svg.svg";
|
||||
import Snapping from "@graphite-frontend/assets/icon-12px-solid/snapping.svg";
|
||||
import SwapHorizontal from "@graphite-frontend/assets/icon-12px-solid/swap-horizontal.svg";
|
||||
import SwapVertical from "@graphite-frontend/assets/icon-12px-solid/swap-vertical.svg";
|
||||
|
|
@ -80,6 +84,10 @@ const SOLID_12PX = {
|
|||
Link: { svg: Link, size: 12 },
|
||||
Overlays: { svg: Overlays, size: 12 },
|
||||
Remove: { svg: Remove, size: 12 },
|
||||
RenderModeNormal: { svg: RenderModeNormal, size: 12 },
|
||||
RenderModeOutline: { svg: RenderModeOutline, size: 12 },
|
||||
RenderModePixels: { svg: RenderModePixels, size: 12 },
|
||||
RenderModeSvg: { svg: RenderModeSvg, size: 12 },
|
||||
Snapping: { svg: Snapping, size: 12 },
|
||||
SwapHorizontal: { svg: SwapHorizontal, size: 12 },
|
||||
SwapVertical: { svg: SwapVertical, size: 12 },
|
||||
|
|
@ -197,9 +205,6 @@ import Trash from "@graphite-frontend/assets/icon-16px-solid/trash.svg";
|
|||
import TurnNegative90 from "@graphite-frontend/assets/icon-16px-solid/turn-negative-90.svg";
|
||||
import TurnPositive90 from "@graphite-frontend/assets/icon-16px-solid/turn-positive-90.svg";
|
||||
import UserManual from "@graphite-frontend/assets/icon-16px-solid/user-manual.svg";
|
||||
import ViewModeNormal from "@graphite-frontend/assets/icon-16px-solid/view-mode-normal.svg";
|
||||
import ViewModeOutline from "@graphite-frontend/assets/icon-16px-solid/view-mode-outline.svg";
|
||||
import ViewModePixels from "@graphite-frontend/assets/icon-16px-solid/view-mode-pixels.svg";
|
||||
import ViewportDesignMode from "@graphite-frontend/assets/icon-16px-solid/viewport-design-mode.svg";
|
||||
import ViewportGuideMode from "@graphite-frontend/assets/icon-16px-solid/viewport-guide-mode.svg";
|
||||
import ViewportSelectMode from "@graphite-frontend/assets/icon-16px-solid/viewport-select-mode.svg";
|
||||
|
|
@ -318,9 +323,6 @@ const SOLID_16PX = {
|
|||
TurnNegative90: { svg: TurnNegative90, size: 16 },
|
||||
TurnPositive90: { svg: TurnPositive90, size: 16 },
|
||||
UserManual: { svg: UserManual, size: 16 },
|
||||
ViewModeNormal: { svg: ViewModeNormal, size: 16 },
|
||||
ViewModeOutline: { svg: ViewModeOutline, size: 16 },
|
||||
ViewModePixels: { svg: ViewModePixels, size: 16 },
|
||||
ViewportDesignMode: { svg: ViewportDesignMode, size: 16 },
|
||||
ViewportGuideMode: { svg: ViewportGuideMode, size: 16 },
|
||||
ViewportSelectMode: { svg: ViewportSelectMode, size: 16 },
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
|||
use glam::{DAffine2, UVec2};
|
||||
use graphene_core::text::FontCache;
|
||||
use graphene_core::transform::Footprint;
|
||||
use graphene_core::vector::style::ViewMode;
|
||||
use graphene_core::vector::style::RenderMode;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
|
@ -240,7 +240,8 @@ pub struct RenderConfig {
|
|||
pub viewport: Footprint,
|
||||
pub export_format: ExportFormat,
|
||||
pub time: TimingInformation,
|
||||
pub view_mode: ViewMode,
|
||||
#[serde(alias = "view_mode")]
|
||||
pub render_mode: RenderMode,
|
||||
pub hide_artboards: bool,
|
||||
pub for_export: bool,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -650,14 +650,16 @@ impl PathStyle {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents different ways of rendering an object
|
||||
/// Ways the user can choose to view the artwork in the viewport.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, specta::Type)]
|
||||
pub enum ViewMode {
|
||||
pub enum RenderMode {
|
||||
/// Render with normal coloration at the current viewport resolution
|
||||
#[default]
|
||||
Normal,
|
||||
Normal = 0,
|
||||
/// Render only the outlines of shapes at the current viewport resolution
|
||||
Outline,
|
||||
/// Render with normal coloration at the document resolution, showing the pixels when the current viewport resolution is higher
|
||||
Pixels,
|
||||
// /// Render with normal coloration at the document resolution, showing the pixels when the current viewport resolution is higher
|
||||
// PixelPreview,
|
||||
// /// Render a preview of how the object would be exported as an SVG.
|
||||
// SvgPreview,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ async fn render<'a: 'n, T: 'n + Render + WasmNotSend>(
|
|||
ctx.footprint();
|
||||
|
||||
let render_params = RenderParams {
|
||||
view_mode: render_config.view_mode,
|
||||
render_mode: render_config.render_mode,
|
||||
hide_artboards: render_config.hide_artboards,
|
||||
for_export: render_config.for_export,
|
||||
footprint,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use glam::DAffine2;
|
|||
use graphene_core::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
|
||||
use graphene_core::gradient::{Gradient, GradientType};
|
||||
use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin, ViewMode};
|
||||
use graphene_core::vector::style::{Fill, PaintOrder, PathStyle, RenderMode, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use std::fmt::Write;
|
||||
|
||||
pub trait RenderExt {
|
||||
|
|
@ -163,9 +163,9 @@ impl RenderExt for PathStyle {
|
|||
/// Renders the shape's fill and stroke attributes as a string with them concatenated together.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn render(&self, svg_defs: &mut String, element_transform: DAffine2, stroke_transform: DAffine2, bounds: DAffine2, transformed_bounds: DAffine2, render_params: &RenderParams) -> String {
|
||||
let view_mode = render_params.view_mode;
|
||||
match view_mode {
|
||||
ViewMode::Outline => {
|
||||
let render_mode = render_params.render_mode;
|
||||
match render_mode {
|
||||
RenderMode::Outline => {
|
||||
let fill_attribute = Fill::None.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params);
|
||||
let mut outline_stroke = Stroke::new(Some(LAYER_OUTLINE_STROKE_COLOR), LAYER_OUTLINE_STROKE_WEIGHT);
|
||||
// Outline strokes should be non-scaling by default
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use graphene_core::transform::{Footprint, Transform};
|
|||
use graphene_core::uuid::{NodeId, generate_uuid};
|
||||
use graphene_core::vector::Vector;
|
||||
use graphene_core::vector::click_target::{ClickTarget, FreePoint};
|
||||
use graphene_core::vector::style::{Fill, PaintOrder, Stroke, StrokeAlign, ViewMode};
|
||||
use graphene_core::vector::style::{Fill, PaintOrder, RenderMode, Stroke, StrokeAlign};
|
||||
use graphene_core::{Artboard, Graphic};
|
||||
use kurbo::Affine;
|
||||
use num_traits::Zero;
|
||||
|
|
@ -159,7 +159,7 @@ pub struct RenderContext {
|
|||
/// Static state used whilst rendering
|
||||
#[derive(Default, Clone)]
|
||||
pub struct RenderParams {
|
||||
pub view_mode: ViewMode,
|
||||
pub render_mode: RenderMode,
|
||||
pub footprint: Footprint,
|
||||
pub thumbnail: bool,
|
||||
/// Don't render the rectangle for an artboard to allow exporting with a transparent background.
|
||||
|
|
@ -555,14 +555,14 @@ impl Render for Table<Graphic> {
|
|||
|
||||
let mut layer = false;
|
||||
|
||||
let blend_mode = match render_params.view_mode {
|
||||
ViewMode::Outline => peniko::Mix::Normal,
|
||||
let blend_mode = match render_params.render_mode {
|
||||
RenderMode::Outline => peniko::Mix::Normal,
|
||||
_ => alpha_blending.blend_mode.to_peniko(),
|
||||
};
|
||||
let mut bounds = RenderBoundingBox::None;
|
||||
|
||||
let opacity = row.alpha_blending.opacity(render_params.for_mask);
|
||||
if opacity < 1. || (render_params.view_mode != ViewMode::Outline && alpha_blending.blend_mode != BlendMode::default()) {
|
||||
if opacity < 1. || (render_params.render_mode != RenderMode::Outline && alpha_blending.blend_mode != BlendMode::default()) {
|
||||
bounds = row.element.bounding_box(transform, true);
|
||||
|
||||
if let RenderBoundingBox::Rectangle(bounds) = bounds {
|
||||
|
|
@ -852,8 +852,8 @@ impl Render for Table<Vector> {
|
|||
}
|
||||
|
||||
// If we're using opacity or a blend mode, we need to push a layer
|
||||
let blend_mode = match render_params.view_mode {
|
||||
ViewMode::Outline => peniko::Mix::Normal,
|
||||
let blend_mode = match render_params.render_mode {
|
||||
RenderMode::Outline => peniko::Mix::Normal,
|
||||
_ => row.alpha_blending.blend_mode.to_peniko(),
|
||||
};
|
||||
let mut layer = false;
|
||||
|
|
@ -969,8 +969,8 @@ impl Render for Table<Vector> {
|
|||
};
|
||||
|
||||
// Render the path
|
||||
match render_params.view_mode {
|
||||
ViewMode::Outline => {
|
||||
match render_params.render_mode {
|
||||
RenderMode::Outline => {
|
||||
let outline_stroke = kurbo::Stroke {
|
||||
width: LAYER_OUTLINE_STROKE_WEIGHT,
|
||||
miter_limit: 4.,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ The right side of the control bar has controls related to the active document an
|
|||
| Overlays | <p>When checked (default), overlays are shown. When unchecked, they are hidden. Overlays are the temporary contextual visualizations (like bounding boxes and vector manipulators) that are usually blue and appear atop the viewport when using tools.</p> |
|
||||
| Snapping | <p>When checked (default), drawing and dragging shapes and vector points means they will snap to other areas of geometric interest like corners or anchor points. When unchecked, the selection moves freely.<br /><br />Fine-grained options are available by clicking the overflow button to access its options popover menu. Each option has a tooltip explaining what it does by hovering the cursor over it.</p><p><img src="https://static.graphite.rs/content/learn/interface/document-panel/snapping-popover__5.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Snapping options popover menu" /></p><p>Snapping options relating to **Bounding Boxes**:</p><p><ul><li>**Align with Edges**: Snaps to horizontal/vertical alignment with the edges of any layer's bounding box.</li><li>**Corner Points**: Snaps to the four corners of any layer's bounding box.</li><li>**Center Points**: Snaps to the center point of any layer's bounding box.</li><li>**Edge Midpoints**: Snaps to any of the four points at the middle of the edges of any layer's bounding box.</li><li>**Distribute Evenly**: Snaps to a consistent distance offset established by the bounding boxes of nearby layers (due to a bug, **Corner Points** and **Center Points** must be enabled).</li></ul></p><p>Snapping options relating to **Paths**:</p><p><ul><li>**Align with Anchor Points**: Snaps to horizontal/vertical alignment with the anchor points of any vector path.</li><li>**Anchor Points**: Snaps to the anchor point of any vector path.</li><li>**Line Midpoints**: Snaps to the point at the middle of any straight line segment of a vector path.</li><li>**Path Intersection Points**: Snaps to any points where vector paths intersect.</li><li>**Along Paths**: Snaps along the length of any vector path.</li><li>**Normal to Paths**: Snaps a line to a point perpendicular to a vector path (due to a bug, **Intersections of Paths** must be enabled).</li><li>**Tangent to Paths**: Snaps a line to a point tangent to a vector path (due to a bug, **Intersections of Paths** must be enabled).</li></ul></p> |
|
||||
| Grid | <p>When checked (off by default), grid lines are shown and snapping to them becomes active. The initial grid scale is 1 document unit, helping you draw pixel-perfect artwork.</p><ul><li><p>**Type** sets whether the grid pattern is made of squares or triangles.</p><p>**Rectangular** is a pattern of horizontal and vertical lines:</p><p><img src="https://static.graphite.rs/content/learn/interface/document-panel/grid-rectangular-popover__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Snapping options popover menu" /></p><p>It has one option unique to this mode:</p><ul><li>**Spacing** is the width and height of the rectangle grid cells.</li></ul><p>**Isometric** is a pattern of triangles:</p><p><img src="https://static.graphite.rs/content/learn/interface/document-panel/grid-isometric-popover__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Snapping options popover menu" /></p><p>It has two options unique to this mode:</p><ul><li>**Y Spacing** is the height between vertical repetitions of the grid.</li><li>**Angles** is the slant of the upward and downward sloped grid lines.</li></ul></li><li>**Display** gives control over the appearance of the grid. The **Display as dotted grid** checkbox (off by default) replaces the solid lines with dots at their intersection points.</li><li>**Origin** is the position in the canvas where the repeating grid pattern begins from. If you need an offset for the grid where an intersection occurs at a specific location, set those coordinates.</li></ul> |
|
||||
| View Mode | <p>**Normal** (default): The artwork is rendered normally.</p><p>**Outline**: The artwork is rendered as a wireframe.</p><p>**Pixels**: **Not implemented yet.** The artwork is rendered as it would appear when exported as a bitmap image at 100% scale regardless of the viewport zoom level.</p> |
|
||||
| Render Mode | <p>**Normal** (default): The artwork is rendered normally.</p><p>**Outline**: The artwork is rendered as a wireframe.</p><p>**Pixel Preview**: **Not implemented yet.** The artwork is rendered as it would appear when exported as a bitmap image at 100% scale regardless of the viewport zoom level.</p><p>**SVG Preview**: **Not implemented yet.** The artwork is rendered as it would appear when exported as an SVG image.</p> |
|
||||
| Zoom In | <p>Zooms the viewport in to the next whole increment.</p> |
|
||||
| Zoom Out | <p>Zooms the viewport out to the next whole increment.</p> |
|
||||
| Reset Tilt and Zoom to 100% | <p>Resets the viewport tilt to 0°. Resets the viewport zoom to 100% which matches the canvas and viewport pixel scale 1:1.</p> |
|
||||
|
|
|
|||