1- use spring_native:: prelude:: { Error , NativeInterfaceRef } ;
1+ use spring_native:: {
2+ prelude:: { Error , NativeInterfaceRef } ,
3+ RmlDataModel , RmlDataVariable ,
4+ } ;
25
36use crate :: sbc:: panels:: field:: {
47 element_by_id, escape_rml, on_change, on_pointer, ChangeQueue , Field , FieldValue ,
@@ -20,6 +23,8 @@ pub(crate) struct ColorField {
2023 channels : [ ChannelElems ; 3 ] ,
2124 expanded : bool ,
2225 drag_channel : Option < usize > , // 0=R, 1=G, 2=B
26+ swatch_color : Option < RmlDataVariable < ' static , String > > ,
27+ channel_values : [ Option < RmlDataVariable < ' static , String > > ; 3 ] ,
2328}
2429
2530struct ChannelElems {
@@ -52,6 +57,8 @@ impl ColorField {
5257 ] ,
5358 expanded : false ,
5459 drag_channel : None ,
60+ swatch_color : None ,
61+ channel_values : [ None , None , None ] ,
5562 tooltip : None ,
5663 }
5764 }
@@ -111,26 +118,39 @@ impl ColorField {
111118
112119 // ── DOM sync helpers ──
113120
114- fn button_rml ( & self ) -> String {
115- format ! (
116- r#"<span class="field-button-title">{title}:</span><span class="field-swatch" style="background-color: {bg};"></span>"# ,
117- title = escape_rml( self . title. trim_end_matches( ':' ) ) ,
118- bg = Self :: to_css( self . value) ,
119- )
121+ fn binding_name ( & self , suffix : & str ) -> String {
122+ let name = self
123+ . name
124+ . chars ( )
125+ . map ( |character| {
126+ if character. is_ascii_alphanumeric ( ) {
127+ character
128+ } else {
129+ '_'
130+ }
131+ } )
132+ . collect :: < String > ( ) ;
133+ format ! ( "field_{name}_{suffix}" )
120134 }
121135
122136 fn sync_swatch ( & self , interface : & NativeInterfaceRef ) {
123- if let Some ( e) = self . swatch {
124- let _ = interface
125- . rml_ui ( )
126- . element_set_inner_rml ( e, & self . button_rml ( ) ) ;
137+ if let Some ( color) = & self . swatch_color {
138+ let _ = color. set ( Self :: to_css ( self . value ) ) ;
139+ } else if let Some ( e) = self . swatch {
140+ let _ = interface. rml_ui ( ) . element_set_attribute (
141+ e,
142+ "style" ,
143+ & format ! ( "background-color: {};" , Self :: to_css( self . value) ) ,
144+ ) ;
127145 }
128146 }
129147
130148 fn sync_channels ( & self , interface : & NativeInterfaceRef ) {
131149 for ( i, ch) in self . channels . iter ( ) . enumerate ( ) {
132150 let val = format ! ( "{:.2}" , self . value[ i] ) ;
133- if let Some ( e) = ch. display {
151+ if let Some ( value) = & self . channel_values [ i] {
152+ let _ = value. set ( val. clone ( ) ) ;
153+ } else if let Some ( e) = ch. display {
134154 let _ = interface. rml_ui ( ) . element_set_inner_rml ( e, & val) ;
135155 }
136156 if let Some ( e) = ch. edit {
@@ -189,20 +209,36 @@ impl Field for ColorField {
189209 self . tooltip . as_deref ( )
190210 }
191211
212+ fn prepare_data_model ( & mut self , model : & RmlDataModel < ' static > ) -> Result < ( ) , Error > {
213+ self . swatch_color =
214+ Some ( model. bind ( & self . binding_name ( "swatch" ) , Self :: to_css ( self . value ) ) ?) ;
215+ for index in 0 ..3 {
216+ self . channel_values [ index] = Some ( model. bind (
217+ & self . binding_name ( & format ! ( "channel_{index}" ) ) ,
218+ format ! ( "{:.2}" , self . value[ index] ) ,
219+ ) ?) ;
220+ }
221+ Ok ( ( ) )
222+ }
223+
192224 fn generate_rml ( & self ) -> String {
193225 let hex = Self :: to_hex ( self . value ) ;
194226 let n = & self . name ;
195227 let channels: String = [ 'r' , 'g' , 'b' ]
196228 . iter ( )
197229 . enumerate ( )
198230 . map ( |( i, & ch) | {
199- let val = format ! ( "{:.2}" , self . value[ i] ) ;
231+ let display = self . channel_values [ i]
232+ . as_ref ( )
233+ . map ( |_| format ! ( "{{{{ {} }}}}" , self . binding_name( & format!( "channel_{i}" ) ) ) )
234+ . unwrap_or_else ( || format ! ( "{:.2}" , self . value[ i] ) ) ;
235+ let input_value = format ! ( "{:.2}" , self . value[ i] ) ;
200236 format ! (
201237 r#"<div class="field-inline"><span class="field-label channel-label {ch}">{ch}</span>"#
202238 ) + & format ! (
203- r#"<button id="field-{n}-{ch}-display" class="field-composite-button field-numeric-button color-channel">{val }</button>"# ,
239+ r#"<button id="field-{n}-{ch}-display" class="field-composite-button field-numeric-button color-channel">{display }</button>"# ,
204240 ) + & format ! (
205- r#"<input type="text" id="field-{n}-{ch}-edit" class="field-input field-numeric-input color-channel hidden" value="{val }"/></div>"# ,
241+ r#"<input type="text" id="field-{n}-{ch}-edit" class="field-input field-numeric-input color-channel hidden" value="{input_value }"/></div>"# ,
206242 )
207243 } )
208244 . collect ( ) ;
@@ -212,8 +248,19 @@ impl Field for ColorField {
212248 // editor below it is native-only (Lua opens a picker dialog instead).
213249 r#"<div class="field-row"><div class="color-field">"# . to_string ( )
214250 + & format ! (
215- r#"<button id="field-{n}-swatch" class="field-composite-button field-color-button">{button}</button>"# ,
216- button = self . button_rml( ) ,
251+ r#"<button id="field-{n}-swatch" class="field-composite-button field-color-button"><span class="field-button-title">{title}:</span><span class="field-swatch" {style}></span></button>"# ,
252+ title = escape_rml( self . title. trim_end_matches( ':' ) ) ,
253+ style = self
254+ . swatch_color
255+ . as_ref( )
256+ . map( |_| format!(
257+ r#"data-style-background-color="{}""# ,
258+ self . binding_name( "swatch" )
259+ ) )
260+ . unwrap_or_else( || format!(
261+ r#"style="background-color: {};""# ,
262+ Self :: to_css( self . value)
263+ ) ) ,
217264 )
218265 + & format ! ( r#"<div id="field-{n}-editor" class="color-editor hidden">"# , )
219266 + & format ! (
0 commit comments