1- use crate :: messages:: frontend:: utility_types:: { ExportBounds , FileType } ;
1+ use crate :: messages:: frontend:: utility_types:: { AnimationExport , ExportBounds , FileType } ;
22use crate :: messages:: layout:: utility_types:: widget_prelude:: * ;
33use crate :: messages:: portfolio:: document:: utility_types:: document_metadata:: LayerNodeIdentifier ;
44use crate :: messages:: prelude:: * ;
@@ -16,6 +16,10 @@ pub struct ExportDialogMessageHandler {
1616 pub bounds : ExportBounds ,
1717 pub artboards : HashMap < LayerNodeIdentifier , String > ,
1818 pub has_selection : bool ,
19+ pub animated : bool ,
20+ pub fps : f64 ,
21+ pub start_seconds : f64 ,
22+ pub end_seconds : f64 ,
1923}
2024
2125impl Default for ExportDialogMessageHandler {
@@ -26,10 +30,21 @@ impl Default for ExportDialogMessageHandler {
2630 bounds : Default :: default ( ) ,
2731 artboards : Default :: default ( ) ,
2832 has_selection : false ,
33+ animated : false ,
34+ fps : 30. ,
35+ start_seconds : 0. ,
36+ end_seconds : 1. ,
2937 }
3038 }
3139}
3240
41+ impl ExportDialogMessageHandler {
42+ fn total_frames ( & self ) -> u32 {
43+ let duration = ( self . end_seconds - self . start_seconds ) . max ( 0. ) ;
44+ ( ( duration * self . fps ) . round ( ) as i64 ) . max ( 1 ) as u32
45+ }
46+ }
47+
3348#[ message_handler_data]
3449impl MessageHandler < ExportDialogMessage , ExportDialogMessageContext < ' _ > > for ExportDialogMessageHandler {
3550 fn process_message ( & mut self , message : ExportDialogMessage , responses : & mut VecDeque < Message > , context : ExportDialogMessageContext ) {
@@ -39,6 +54,15 @@ impl MessageHandler<ExportDialogMessage, ExportDialogMessageContext<'_>> for Exp
3954 ExportDialogMessage :: FileType { file_type } => self . file_type = file_type,
4055 ExportDialogMessage :: ScaleFactor { factor } => self . scale_factor = factor,
4156 ExportDialogMessage :: ExportBounds { bounds } => self . bounds = bounds,
57+ ExportDialogMessage :: Animated { animated } => self . animated = animated,
58+ ExportDialogMessage :: Fps { fps } => self . fps = fps. max ( 0.001 ) ,
59+ ExportDialogMessage :: StartSeconds { start } => {
60+ self . start_seconds = start. max ( 0. ) ;
61+ if self . end_seconds < self . start_seconds {
62+ self . end_seconds = self . start_seconds ;
63+ }
64+ }
65+ ExportDialogMessage :: EndSeconds { end } => self . end_seconds = end. max ( self . start_seconds ) ,
4266
4367 ExportDialogMessage :: Submit => {
4468 // Fall back to "All Artwork" if "Selection" was chosen but nothing is currently selected
@@ -52,13 +76,21 @@ impl MessageHandler<ExportDialogMessage, ExportDialogMessageContext<'_>> for Exp
5276 ExportBounds :: Artboard ( layer) => self . artboards . get ( & layer) . cloned ( ) ,
5377 _ => None ,
5478 } ;
79+
80+ let animation = self . animated . then ( || AnimationExport {
81+ fps : self . fps ,
82+ start_seconds : self . start_seconds ,
83+ total_frames : self . total_frames ( ) ,
84+ } ) ;
85+
5586 responses. add_front ( PortfolioMessage :: SubmitDocumentExport {
5687 name : portfolio. active_document ( ) . map ( |document| document. name . clone ( ) ) . unwrap_or_default ( ) ,
5788 file_type : self . file_type ,
5889 scale_factor : self . scale_factor ,
5990 bounds,
6091 artboard_name,
6192 artboard_count : self . artboards . len ( ) ,
93+ animation,
6294 } )
6395 }
6496 }
@@ -163,6 +195,74 @@ impl LayoutHolder for ExportDialogMessageHandler {
163195 DropdownInput :: new( entries) . selected_index( Some ( index as u32 ) ) . widget_instance( ) ,
164196 ] ;
165197
166- Layout ( vec ! [ LayoutGroup :: row( export_type) , LayoutGroup :: row( resolution) , LayoutGroup :: row( export_area) ] )
198+ let animation_checkbox_id = CheckboxId :: new ( ) ;
199+ let animation_toggle = vec ! [
200+ TextLabel :: new( "Animation" ) . table_align( true ) . min_width( 100 ) . for_checkbox( animation_checkbox_id) . widget_instance( ) ,
201+ Separator :: new( SeparatorStyle :: Unrelated ) . widget_instance( ) ,
202+ CheckboxInput :: new( self . animated)
203+ . on_update( |checkbox_input: & CheckboxInput | ExportDialogMessage :: Animated { animated: checkbox_input. checked } . into( ) )
204+ . for_label( animation_checkbox_id)
205+ . widget_instance( ) ,
206+ ] ;
207+
208+ let mut layout_groups = vec ! [
209+ LayoutGroup :: row( export_type) ,
210+ LayoutGroup :: row( resolution) ,
211+ LayoutGroup :: row( export_area) ,
212+ LayoutGroup :: row( animation_toggle) ,
213+ ] ;
214+
215+ if self . animated {
216+ let fps_row = vec ! [
217+ TextLabel :: new( "FPS" ) . table_align( true ) . min_width( 100 ) . widget_instance( ) ,
218+ Separator :: new( SeparatorStyle :: Unrelated ) . widget_instance( ) ,
219+ NumberInput :: new( Some ( self . fps) )
220+ . unit( " fps" )
221+ . min( 0.001 )
222+ . max( 1000. )
223+ . increment_step( 1. )
224+ . on_update( |number_input: & NumberInput | ExportDialogMessage :: Fps { fps: number_input. value. unwrap( ) } . into( ) )
225+ . min_width( 200 )
226+ . widget_instance( ) ,
227+ ] ;
228+
229+ let start_row = vec ! [
230+ TextLabel :: new( "Start" ) . table_align( true ) . min_width( 100 ) . widget_instance( ) ,
231+ Separator :: new( SeparatorStyle :: Unrelated ) . widget_instance( ) ,
232+ NumberInput :: new( Some ( self . start_seconds) )
233+ . unit( " sec" )
234+ . min( 0. )
235+ . increment_step( 0.1 )
236+ . on_update( |number_input: & NumberInput | ExportDialogMessage :: StartSeconds { start: number_input. value. unwrap( ) } . into( ) )
237+ . min_width( 200 )
238+ . widget_instance( ) ,
239+ ] ;
240+
241+ let end_row = vec ! [
242+ TextLabel :: new( "End" ) . table_align( true ) . min_width( 100 ) . widget_instance( ) ,
243+ Separator :: new( SeparatorStyle :: Unrelated ) . widget_instance( ) ,
244+ NumberInput :: new( Some ( self . end_seconds) )
245+ . unit( " sec" )
246+ . min( self . start_seconds)
247+ . increment_step( 0.1 )
248+ . on_update( |number_input: & NumberInput | ExportDialogMessage :: EndSeconds { end: number_input. value. unwrap( ) } . into( ) )
249+ . min_width( 200 )
250+ . widget_instance( ) ,
251+ ] ;
252+
253+ let frame_count = self . total_frames ( ) ;
254+ let frames_row = vec ! [
255+ TextLabel :: new( "Frames" ) . table_align( true ) . min_width( 100 ) . widget_instance( ) ,
256+ Separator :: new( SeparatorStyle :: Unrelated ) . widget_instance( ) ,
257+ TextLabel :: new( format!( "{frame_count} frame{}" , if frame_count == 1 { "" } else { "s" } ) ) . widget_instance( ) ,
258+ ] ;
259+
260+ layout_groups. push ( LayoutGroup :: row ( fps_row) ) ;
261+ layout_groups. push ( LayoutGroup :: row ( start_row) ) ;
262+ layout_groups. push ( LayoutGroup :: row ( end_row) ) ;
263+ layout_groups. push ( LayoutGroup :: row ( frames_row) ) ;
264+ }
265+
266+ Layout ( layout_groups)
167267 }
168268}
0 commit comments