22
33use eframe:: egui;
44use rysyn_ffi_bridge:: { init_bridge, get_bridge, Command , StateSnapshot } ;
5+ use std:: sync:: mpsc:: { channel, Sender , Receiver } ;
56
67use crate :: panels:: {
78 transport:: TransportPanel ,
@@ -17,6 +18,10 @@ pub struct RysynApp {
1718 /// Current state from audio core
1819 state : StateSnapshot ,
1920
21+ /// Command sender for UI -> Audio Core
22+ cmd_tx : Sender < Command > ,
23+ cmd_rx : Receiver < Command > ,
24+
2025 /// Panel states
2126 transport : TransportPanel ,
2227 timeline : TimelinePanel ,
@@ -40,8 +45,12 @@ impl RysynApp {
4045 // Initialize FFI bridge
4146 init_bridge ( ) ;
4247
48+ let ( cmd_tx, cmd_rx) = channel ( ) ;
49+
4350 Self {
4451 state : StateSnapshot :: new ( ) ,
52+ cmd_tx,
53+ cmd_rx,
4554 transport : TransportPanel :: default ( ) ,
4655 timeline : TimelinePanel :: default ( ) ,
4756 track_list : TrackListPanel :: default ( ) ,
@@ -56,9 +65,11 @@ impl RysynApp {
5665 }
5766 }
5867
59- fn send_command ( & self , cmd : Command ) {
68+ fn process_commands ( & self ) {
6069 if let Some ( bridge) = get_bridge ( ) {
61- let _ = bridge. send_command ( cmd) ;
70+ while let Ok ( cmd) = self . cmd_rx . try_recv ( ) {
71+ let _ = bridge. send_command ( cmd) ;
72+ }
6273 }
6374 }
6475
@@ -74,23 +85,30 @@ impl eframe::App for RysynApp {
7485 // Refresh state from audio core
7586 self . refresh_state ( ) ;
7687
88+ // Process any pending commands
89+ self . process_commands ( ) ;
90+
7791 // Request continuous repaint for smooth animation
7892 ctx. request_repaint ( ) ;
7993
94+ // Clone cmd_tx for use in closures
95+ let cmd_tx = self . cmd_tx . clone ( ) ;
96+
8097 // === Menu Bar ===
8198 egui:: TopBottomPanel :: top ( "menu_bar" ) . show ( ctx, |ui| {
99+ let cmd_tx = cmd_tx. clone ( ) ;
82100 egui:: menu:: bar ( ui, |ui| {
83101 ui. menu_button ( "File" , |ui| {
84102 if ui. button ( "New Project" ) . clicked ( ) {
85- self . send_command ( Command :: NewProject ) ;
103+ let _ = cmd_tx . send ( Command :: NewProject ) ;
86104 ui. close_menu ( ) ;
87105 }
88106 if ui. button ( "Open Project..." ) . clicked ( ) {
89107 // TODO: File dialog
90108 ui. close_menu ( ) ;
91109 }
92110 if ui. button ( "Save Project" ) . clicked ( ) {
93- self . send_command ( Command :: SaveProject { path : None } ) ;
111+ let _ = cmd_tx . send ( Command :: SaveProject { path : None } ) ;
94112 ui. close_menu ( ) ;
95113 }
96114 ui. separator ( ) ;
@@ -101,32 +119,32 @@ impl eframe::App for RysynApp {
101119
102120 ui. menu_button ( "Edit" , |ui| {
103121 if ui. button ( "Undo" ) . clicked ( ) {
104- self . send_command ( Command :: Undo ) ;
122+ let _ = cmd_tx . send ( Command :: Undo ) ;
105123 ui. close_menu ( ) ;
106124 }
107125 if ui. button ( "Redo" ) . clicked ( ) {
108- self . send_command ( Command :: Redo ) ;
126+ let _ = cmd_tx . send ( Command :: Redo ) ;
109127 ui. close_menu ( ) ;
110128 }
111129 } ) ;
112130
113131 ui. menu_button ( "Track" , |ui| {
114132 if ui. button ( "Add Audio Track" ) . clicked ( ) {
115- self . send_command ( Command :: CreateTrack {
133+ let _ = cmd_tx . send ( Command :: CreateTrack {
116134 name : "Audio" . to_string ( ) ,
117135 is_midi : false
118136 } ) ;
119137 ui. close_menu ( ) ;
120138 }
121139 if ui. button ( "Add MIDI Track" ) . clicked ( ) {
122- self . send_command ( Command :: CreateTrack {
140+ let _ = cmd_tx . send ( Command :: CreateTrack {
123141 name : "MIDI" . to_string ( ) ,
124142 is_midi : true
125143 } ) ;
126144 ui. close_menu ( ) ;
127145 }
128146 if ui. button ( "Add Instrument Track" ) . clicked ( ) {
129- self . send_command ( Command :: CreateTrack {
147+ let _ = cmd_tx . send ( Command :: CreateTrack {
130148 name : "Instrument" . to_string ( ) ,
131149 is_midi : true
132150 } ) ;
@@ -159,67 +177,76 @@ impl eframe::App for RysynApp {
159177 } ) ;
160178 } ) ;
161179
180+ // Clone state for panels (to avoid borrow issues)
181+ let state = self . state . clone ( ) ;
182+
162183 // === Transport Bar ===
184+ let cmd_tx2 = cmd_tx. clone ( ) ;
163185 egui:: TopBottomPanel :: top ( "transport_bar" )
164186 . min_height ( 60.0 )
165187 . show ( ctx, |ui| {
166- self . transport . show ( ui, & self . state , |cmd| self . send_command ( cmd) ) ;
188+ self . transport . show ( ui, & state, |cmd| { let _ = cmd_tx2 . send ( cmd) ; } ) ;
167189 } ) ;
168190
169191 // === Bottom Panel (Mixer) ===
170192 if self . show_mixer {
193+ let cmd_tx2 = cmd_tx. clone ( ) ;
171194 egui:: TopBottomPanel :: bottom ( "mixer_panel" )
172195 . resizable ( true )
173196 . default_height ( 200.0 )
174197 . min_height ( 100.0 )
175198 . show ( ctx, |ui| {
176- self . mixer . show ( ui, & self . state , |cmd| self . send_command ( cmd) ) ;
199+ self . mixer . show ( ui, & state, |cmd| { let _ = cmd_tx2 . send ( cmd) ; } ) ;
177200 } ) ;
178201 }
179202
180203 // === Left Panel (Browser) ===
181204 if self . show_browser {
205+ let cmd_tx2 = cmd_tx. clone ( ) ;
182206 egui:: SidePanel :: left ( "browser_panel" )
183207 . resizable ( true )
184208 . default_width ( 200.0 )
185209 . min_width ( 150.0 )
186210 . show ( ctx, |ui| {
187- self . browser . show ( ui, & self . state , |cmd| self . send_command ( cmd) ) ;
211+ self . browser . show ( ui, & state, |cmd| { let _ = cmd_tx2 . send ( cmd) ; } ) ;
188212 } ) ;
189213 }
190214
191215 // === Right Panel (Inspector) ===
192216 if self . show_inspector {
217+ let cmd_tx2 = cmd_tx. clone ( ) ;
193218 egui:: SidePanel :: right ( "inspector_panel" )
194219 . resizable ( true )
195220 . default_width ( 250.0 )
196221 . min_width ( 200.0 )
197222 . show ( ctx, |ui| {
198- self . inspector . show ( ui, & self . state , |cmd| self . send_command ( cmd) ) ;
223+ self . inspector . show ( ui, & state, |cmd| { let _ = cmd_tx2 . send ( cmd) ; } ) ;
199224 } ) ;
200225 }
201226
202227 // === Central Area (Track List + Timeline) ===
228+ let cmd_tx2 = cmd_tx. clone ( ) ;
229+ let cmd_tx3 = cmd_tx. clone ( ) ;
203230 egui:: CentralPanel :: default ( ) . show ( ctx, |ui| {
204231 ui. horizontal ( |ui| {
205232 // Track list (left side, fixed width)
206- egui:: Frame :: none ( )
233+ egui:: Frame :: new ( )
207234 . fill ( ui. visuals ( ) . extreme_bg_color )
208235 . show ( ui, |ui| {
209236 ui. set_width ( 200.0 ) ;
210237 self . track_list . show (
211238 ui,
212- & self . state ,
213- |cmd| self . send_command ( cmd)
239+ & state,
240+ |cmd| { let _ = cmd_tx2 . send ( cmd) ; }
214241 ) ;
215242 } ) ;
216243
217244 // Timeline (right side, fills remaining space)
218245 ui. with_layout ( egui:: Layout :: left_to_right ( egui:: Align :: Min ) . with_main_wrap ( false ) , |ui| {
219246 self . timeline . show (
220247 ui,
221- & self . state ,
222- |cmd| self . send_command ( cmd) ,
248+ & state,
249+ |cmd| { let _ = cmd_tx3 . send ( cmd) ; } ,
223250 & mut self . timeline_zoom ,
224251 & mut self . timeline_scroll
225252 ) ;
0 commit comments