Skip to content

Commit da45ab2

Browse files
authored
Add a checkered background to transparent artboards and the infinite canvas (#4022)
* Add checkered transparency rendering to infinite canvas and artboards * Enable artboard clipping by default * Make new infinite canvas documents begin with a white background layer * Remove the export dialog's transparency option now that it's redundant * Make exporting transparent JPGs use white not black * Code review
1 parent 661e8bc commit da45ab2

File tree

21 files changed

+244
-99
lines changed

21 files changed

+244
-99
lines changed

desktop/src/render/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl RenderState {
236236
return;
237237
};
238238
let size = glam::UVec2::new(viewport_texture.width(), viewport_texture.height());
239-
let result = futures::executor::block_on(self.executor.render_vello_scene_to_target_texture(&scene, size, &Default::default(), None, &mut self.overlays_texture));
239+
let result = futures::executor::block_on(self.executor.render_vello_scene_to_target_texture(&scene, size, &Default::default(), &mut self.overlays_texture));
240240
if let Err(e) = result {
241241
tracing::error!("Error rendering overlays: {:?}", e);
242242
return;

editor/src/messages/dialog/export_dialog/export_dialog_message.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::messages::prelude::*;
66
pub enum ExportDialogMessage {
77
FileType { file_type: FileType },
88
ScaleFactor { factor: f64 },
9-
TransparentBackground { transparent: bool },
109
ExportBounds { bounds: ExportBounds },
1110

1211
Submit,

editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub struct ExportDialogMessageHandler {
1414
pub file_type: FileType,
1515
pub scale_factor: f64,
1616
pub bounds: ExportBounds,
17-
pub transparent_background: bool,
1817
pub artboards: HashMap<LayerNodeIdentifier, String>,
1918
pub has_selection: bool,
2019
}
@@ -25,7 +24,6 @@ impl Default for ExportDialogMessageHandler {
2524
file_type: Default::default(),
2625
scale_factor: 1.,
2726
bounds: Default::default(),
28-
transparent_background: false,
2927
artboards: Default::default(),
3028
has_selection: false,
3129
}
@@ -40,20 +38,25 @@ impl MessageHandler<ExportDialogMessage, ExportDialogMessageContext<'_>> for Exp
4038
match message {
4139
ExportDialogMessage::FileType { file_type } => self.file_type = file_type,
4240
ExportDialogMessage::ScaleFactor { factor } => self.scale_factor = factor,
43-
ExportDialogMessage::TransparentBackground { transparent } => self.transparent_background = transparent,
4441
ExportDialogMessage::ExportBounds { bounds } => self.bounds = bounds,
4542

4643
ExportDialogMessage::Submit => {
47-
let artboard_name = match self.bounds {
44+
// Fall back to "All Artwork" if "Selection" was chosen but nothing is currently selected
45+
let bounds = if !self.has_selection && self.bounds == ExportBounds::Selection {
46+
ExportBounds::AllArtwork
47+
} else {
48+
self.bounds
49+
};
50+
51+
let artboard_name = match bounds {
4852
ExportBounds::Artboard(layer) => self.artboards.get(&layer).cloned(),
4953
_ => None,
5054
};
5155
responses.add_front(PortfolioMessage::SubmitDocumentExport {
5256
name: portfolio.active_document().map(|document| document.name.clone()).unwrap_or_default(),
5357
file_type: self.file_type,
5458
scale_factor: self.scale_factor,
55-
bounds: self.bounds,
56-
transparent_background: self.file_type != FileType::Jpg && self.transparent_background,
59+
bounds,
5760
artboard_name,
5861
artboard_count: self.artboards.len(),
5962
})
@@ -127,6 +130,7 @@ impl LayoutHolder for ExportDialogMessageHandler {
127130
let artboards = self.artboards.iter().map(|(&layer, name)| (ExportBounds::Artboard(layer), name.to_string(), false)).collect();
128131
let choices = [standard_bounds, artboards];
129132

133+
// Fall back to "All Artwork" if "Selection" was chosen but nothing is currently selected
130134
let current_bounds = if !self.has_selection && self.bounds == ExportBounds::Selection {
131135
ExportBounds::AllArtwork
132136
} else {
@@ -159,22 +163,6 @@ impl LayoutHolder for ExportDialogMessageHandler {
159163
DropdownInput::new(entries).selected_index(Some(index as u32)).widget_instance(),
160164
];
161165

162-
let checkbox_id = CheckboxId::new();
163-
let transparent_background = vec![
164-
TextLabel::new("Transparency").table_align(true).min_width(100).for_checkbox(checkbox_id).widget_instance(),
165-
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
166-
CheckboxInput::new(self.transparent_background)
167-
.disabled(self.file_type == FileType::Jpg)
168-
.on_update(move |value: &CheckboxInput| ExportDialogMessage::TransparentBackground { transparent: value.checked }.into())
169-
.for_label(checkbox_id)
170-
.widget_instance(),
171-
];
172-
173-
Layout(vec![
174-
LayoutGroup::row(export_type),
175-
LayoutGroup::row(resolution),
176-
LayoutGroup::row(export_area),
177-
LayoutGroup::row(transparent_background),
178-
])
166+
Layout(vec![LayoutGroup::row(export_type), LayoutGroup::row(resolution), LayoutGroup::row(export_area)])
179167
}
180168
}

editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::messages::layout::utility_types::widget_prelude::*;
2+
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
23
use crate::messages::prelude::*;
34
use glam::{IVec2, UVec2};
45
use graph_craft::document::NodeId;
6+
use graphene_std::Color;
57

68
/// A dialog to allow users to set some initial options about a new document.
79
#[derive(Debug, Clone, Default, ExtractField)]
@@ -22,25 +24,39 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
2224
NewDocumentDialogMessage::Submit => {
2325
responses.add(PortfolioMessage::NewDocumentWithName { name: self.name.clone() });
2426

25-
let create_artboard = !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0;
26-
if create_artboard {
27+
if self.infinite {
28+
// Infinite canvas: add a locked white background layer
29+
let node_id = NodeId::new();
30+
responses.add(GraphOperationMessage::NewColorFillLayer {
31+
node_id,
32+
color: Color::WHITE,
33+
parent: LayerNodeIdentifier::ROOT_PARENT,
34+
insert_index: 0,
35+
});
36+
responses.add(NodeGraphMessage::SetDisplayNameImpl {
37+
node_id,
38+
alias: "Background".to_string(),
39+
});
40+
responses.add(NodeGraphMessage::SetLocked { node_id, locked: true });
41+
} else if self.dimensions.x > 0 && self.dimensions.y > 0 {
42+
// Finite canvas: create an artboard with the specified dimensions
2743
responses.add(GraphOperationMessage::NewArtboard {
2844
id: NodeId::new(),
2945
artboard: graphene_std::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
3046
});
3147
responses.add(NavigationMessage::CanvasPan { delta: self.dimensions.as_dvec2() });
32-
responses.add(NodeGraphMessage::RunDocumentGraph);
48+
}
3349

34-
responses.add(ViewportMessage::RepropagateUpdate);
50+
responses.add(NodeGraphMessage::RunDocumentGraph);
51+
responses.add(ViewportMessage::RepropagateUpdate);
3552

36-
responses.add(DeferMessage::AfterNavigationReady {
37-
messages: vec![
38-
DocumentMessage::ZoomCanvasToFitAll.into(),
39-
DocumentMessage::DeselectAllLayers.into(),
40-
PortfolioMessage::AutoSaveActiveDocument.into(),
41-
],
42-
});
43-
}
53+
responses.add(DeferMessage::AfterNavigationReady {
54+
messages: vec![
55+
DocumentMessage::ZoomCanvasToFitAll.into(),
56+
DocumentMessage::DeselectAllLayers.into(),
57+
PortfolioMessage::AutoSaveActiveDocument.into(),
58+
],
59+
});
4460

4561
responses.add(DocumentMessage::MarkAsSaved);
4662
}

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,8 +1400,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
14001400
let node_layer_id = LayerNodeIdentifier::new_unchecked(node_id);
14011401
let new_artboard_node = document_node_definitions::resolve_network_node_type("Artboard")
14021402
.expect("Failed to create artboard node")
1403-
// Enable clipping by default (input index 5) so imported content is masked to the artboard bounds
1404-
.node_template_input_override([None, None, None, None, None, Some(NodeInput::value(TaggedValue::Bool(true), false))]);
1403+
.default_node_template();
14051404
responses.add(NodeGraphMessage::InsertNode {
14061405
node_id,
14071406
node_template: Box::new(new_artboard_node),

editor/src/messages/portfolio/document/graph_operation/graph_operation_message.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node
44
use crate::messages::prelude::*;
55
use glam::{DAffine2, IVec2};
66
use graph_craft::document::NodeId;
7-
use graphene_std::Artboard;
87
use graphene_std::brush::brush_stroke::BrushStroke;
98
use graphene_std::raster::BlendMode;
109
use graphene_std::raster_types::{CPU, Raster};
@@ -14,6 +13,7 @@ use graphene_std::text::{Font, TypesettingConfig};
1413
use graphene_std::vector::PointId;
1514
use graphene_std::vector::VectorModificationType;
1615
use graphene_std::vector::style::{Fill, Stroke};
16+
use graphene_std::{Artboard, Color};
1717

1818
#[impl_message(Message, DocumentMessage, GraphOperation)]
1919
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -97,6 +97,12 @@ pub enum GraphOperationMessage {
9797
parent: LayerNodeIdentifier,
9898
insert_index: usize,
9999
},
100+
NewColorFillLayer {
101+
node_id: NodeId,
102+
color: Color,
103+
parent: LayerNodeIdentifier,
104+
insert_index: usize,
105+
},
100106
NewVectorLayer {
101107
id: NodeId,
102108
subpaths: Vec<Subpath<PointId>>,

editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,13 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
274274
responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index });
275275
responses.add(NodeGraphMessage::RunDocumentGraph);
276276
}
277+
GraphOperationMessage::NewColorFillLayer { node_id, color, parent, insert_index } => {
278+
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
279+
let layer = modify_inputs.create_layer(node_id);
280+
modify_inputs.insert_color_value(color, layer);
281+
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
282+
responses.add(NodeGraphMessage::RunDocumentGraph);
283+
}
277284
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
278285
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
279286
let layer = modify_inputs.create_layer(id);

editor/src/messages/portfolio/document/graph_operation/utility_types.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use glam::{DAffine2, IVec2};
77
use graph_craft::document::value::TaggedValue;
88
use graph_craft::document::{NodeId, NodeInput};
99
use graph_craft::{ProtoNodeIdentifier, concrete};
10-
use graphene_std::Artboard;
1110
use graphene_std::brush::brush_stroke::BrushStroke;
1211
use graphene_std::raster::BlendMode;
1312
use graphene_std::raster_types::{CPU, Raster};
@@ -17,7 +16,7 @@ use graphene_std::text::{Font, TypesettingConfig};
1716
use graphene_std::vector::Vector;
1817
use graphene_std::vector::style::{Fill, Stroke};
1918
use graphene_std::vector::{PointId, VectorModificationType};
20-
use graphene_std::{Graphic, NodeInputDecleration};
19+
use graphene_std::{Artboard, Color, Graphic, NodeInputDecleration};
2120

2221
#[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
2322
pub enum TransformIn {
@@ -289,6 +288,19 @@ impl<'a> ModifyInputsContext<'a> {
289288
self.network_interface.move_node_to_chain_start(&fill_id, layer, &[], self.import);
290289
}
291290

291+
pub fn insert_color_value(&mut self, color: Color, layer: LayerNodeIdentifier) {
292+
let color_value = resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER)
293+
.expect("Color Value node does not exist")
294+
.node_template_input_override([
295+
Some(NodeInput::value(TaggedValue::None, false)),
296+
Some(NodeInput::value(TaggedValue::Color(Table::new_from_element(color)), false)),
297+
]);
298+
299+
let color_value_id = NodeId::new();
300+
self.network_interface.insert_node(color_value_id, color_value, &[]);
301+
self.network_interface.move_node_to_chain_start(&color_value_id, layer, &[], self.import);
302+
}
303+
292304
pub fn insert_image_data(&mut self, image_frame: Table<Raster<CPU>>, layer: LayerNodeIdentifier) {
293305
let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
294306
let image = resolve_proto_node_type(graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER)

editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
380380
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
381381
NodeInput::value(TaggedValue::DVec2(DVec2::new(1920., 1080.)), false),
382382
NodeInput::value(TaggedValue::Color(Table::new_from_element(Color::WHITE)), false),
383-
NodeInput::value(TaggedValue::Bool(false), false),
383+
NodeInput::value(TaggedValue::Bool(true), false),
384384
],
385385
..Default::default()
386386
},

editor/src/messages/portfolio/portfolio_message.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ pub enum PortfolioMessage {
168168
file_type: FileType,
169169
scale_factor: f64,
170170
bounds: ExportBounds,
171-
transparent_background: bool,
172171
artboard_name: Option<String>,
173172
artboard_count: usize,
174173
},

0 commit comments

Comments
 (0)