Skip to content

Commit e5f1793

Browse files
committed
Implement Copy to all widgets, eliminate widget clone before use it in clsoures
1 parent f5211c1 commit e5f1793

File tree

123 files changed

+983
-1488
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

123 files changed

+983
-1488
lines changed

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010

1111
## Why Choose wxDragon?
1212

13-
🎯 **Native Look & Feel** - Your apps integrate perfectly with each operating system's design language
14-
🚀 **Single Codebase** - Write once, run everywhere with true cross-platform compatibility
15-
🛡️ **Memory Safe** - All the safety guarantees of Rust with the mature wxWidgets foundation
16-
**High Performance** - Direct access to native GUI components with minimal overhead
17-
🎨 **Rich Widget Set** - Comprehensive collection of native controls and layouts
13+
🎯 **Native Look & Feel** - Your apps integrate perfectly with each operating system's design language
14+
🚀 **Single Codebase** - Write once, run everywhere with true cross-platform compatibility
15+
🛡️ **Memory Safe** - All the safety guarantees of Rust with the mature wxWidgets foundation
16+
**High Performance** - Direct access to native GUI components with minimal overhead
17+
🎨 **Rich Widget Set** - Comprehensive collection of native controls and layouts
1818
🔧 **Two Development Styles** - Choose between programmatic creation or visual XRC design
19+
**Ergonomic API** - Widgets implement `Copy`, so no manual cloning needed for closures
1920

2021
## Screenshots
2122

@@ -112,17 +113,15 @@ fn main() {
112113
let ui = MyUI::new(None);
113114

114115
// Access widgets with full type safety
115-
let button = &ui.hello_button; // Button
116-
let input = &ui.input_field; // TextCtrl
117-
let label = &ui.status_label; // StaticText
118-
let frame = &ui.main_frame; // Frame
119-
120-
// Bind events with closures
121-
let label_clone = label.clone();
122-
let input_clone = input.clone();
116+
let button = ui.hello_button; // Button
117+
let input = ui.input_field; // TextCtrl
118+
let label = ui.status_label; // StaticText
119+
let frame = ui.main_frame; // Frame
120+
121+
// Bind events with closures - widgets are Copy, no cloning needed!
123122
button.on_click(move |_| {
124-
let text = input_clone.get_value();
125-
label_clone.set_label(&format!("You entered: {}", text));
123+
let text = input.get_value();
124+
label.set_label(&format!("You entered: {}", text));
126125
});
127126

128127
frame.show(true);
@@ -136,6 +135,7 @@ fn main() {
136135
- **Type Safety** - Compile-time checking of widget names and types
137136
- **Clean Separation** - UI layout separate from application logic
138137
- **Professional Workflows** - Integrate with existing design tools
138+
- **Zero Boilerplate** - Widgets are `Copy`, use them directly in closures without `.clone()`
139139

140140
## Platform Support
141141

examples/rust/clipboard_test/src/main.rs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,20 +98,19 @@ fn main() {
9898
let clipboard = Clipboard::get();
9999

100100
// Implement button event handlers
101-
let text_ctrl_copy = text_ctrl.clone();
101+
// Widgets are Copy, so they can be used directly in closures
102102
copy_button.on_click(move |_| {
103-
let text = text_ctrl_copy.get_value();
103+
let text = text_ctrl.get_value();
104104
if clipboard.set_text(&text) {
105105
println!("Text copied to clipboard: {text}");
106106
} else {
107107
println!("Failed to copy text to clipboard");
108108
}
109109
});
110110

111-
let text_ctrl_paste = text_ctrl.clone();
112111
paste_button.on_click(move |_| {
113112
if let Some(text) = clipboard.get_text() {
114-
text_ctrl_paste.set_value(&text);
113+
text_ctrl.set_value(&text);
115114
println!("Text pasted from clipboard: {text}");
116115
} else {
117116
println!("No text on clipboard or clipboard access failed");
@@ -143,8 +142,7 @@ fn main() {
143142
}
144143
});
145144

146-
// For paste bitmap button - this should now work correctly with StaticBitmap's proper Clone
147-
let static_bitmap_clone = static_bitmap.clone();
145+
// For paste bitmap button - widgets are Copy
148146
paste_bitmap_button.on_click(move |_| {
149147
// Check if bitmap format is supported
150148
if !clipboard.is_format_supported(DataFormat::BITMAP) {
@@ -159,8 +157,7 @@ fn main() {
159157
if let Some(_locker) = clipboard.locker() {
160158
if clipboard.get_data(&bitmap_data) {
161159
if let Some(bitmap) = bitmap_data.get_bitmap() {
162-
// Now static_bitmap_clone is a proper StaticBitmap, not just a Window
163-
static_bitmap_clone.set_bitmap(&bitmap);
160+
static_bitmap.set_bitmap(&bitmap);
164161
println!("Bitmap pasted from clipboard");
165162
}
166163
} else {
@@ -199,42 +196,40 @@ fn main() {
199196
});
200197

201198
// Paste files from clipboard
202-
let clipboard_paste = clipboard;
203-
let file_text_ctrl_paste = file_text_ctrl.clone();
204199
paste_file_button.on_click(move |_| {
205-
if let Some(_locker) = clipboard_paste.locker() {
200+
if let Some(_locker) = clipboard.locker() {
206201
// Check if file format is supported
207-
if clipboard_paste.is_format_supported(DataFormat::FILENAME) {
202+
if clipboard.is_format_supported(DataFormat::FILENAME) {
208203
// Create a file data object to receive the data
209204
let file_data = FileDataObject::new();
210205

211206
// Try getting the data - this should now work with our fixed implementation
212-
if clipboard_paste.get_data(&file_data) {
207+
if clipboard.get_data(&file_data) {
213208
// Get all files from the data object
214209
let files = file_data.get_files();
215210

216211
if files.is_empty() {
217212
println!("No files on clipboard");
218-
file_text_ctrl_paste.set_value("No files on clipboard");
213+
file_text_ctrl.set_value("No files on clipboard");
219214
} else {
220215
// Display the file paths in the text control
221216
let file_list = files.join("\n");
222217
println!("Files pasted from clipboard:\n{file_list}");
223-
file_text_ctrl_paste.set_value(&file_list);
218+
file_text_ctrl.set_value(&file_list);
224219
}
225220
} else {
226221
// Fallback to text for files
227-
if let Some(text) = clipboard_paste.get_text() {
222+
if let Some(text) = clipboard.get_text() {
228223
println!("Retrieved file path as text: {text}");
229-
file_text_ctrl_paste.set_value(&text);
224+
file_text_ctrl.set_value(&text);
230225
} else {
231226
println!("Failed to get files from clipboard");
232-
file_text_ctrl_paste.set_value("Failed to get files from clipboard");
227+
file_text_ctrl.set_value("Failed to get files from clipboard");
233228
}
234229
}
235230
} else {
236231
println!("No files on clipboard");
237-
file_text_ctrl_paste.set_value("No files on clipboard");
232+
file_text_ctrl.set_value("No files on clipboard");
238233
}
239234
}
240235
});

examples/rust/combobox_modifier_test/src/main.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ fn main() {
4646
let status_label = StaticText::builder(&panel).with_label("Status: Ready").build();
4747

4848
// Test key events on the ComboBox
49-
let status_clone = status_label.clone();
50-
let combo_clone = combo_box.clone();
49+
// Widgets are Copy, so they can be used directly in closures
5150
combo_box.on_key_down(move |event| {
5251
if let WindowEventData::Keyboard(ref key_event) = event {
5352
let mut should_handle = false;
@@ -56,7 +55,7 @@ fn main() {
5655
if let Some(key_code) = key_event.get_key_code() {
5756
// Check for CTRL+BACKSPACE (key code 8 is backspace)
5857
if key_code == 8 && key_event.control_down() {
59-
delete_word_back(&combo_clone);
58+
delete_word_back(&combo_box);
6059
status_text = "CTRL+BACKSPACE: Deleted word back".to_string();
6160
should_handle = true;
6261
}
@@ -86,7 +85,7 @@ fn main() {
8685

8786
// Update status if we detected something interesting
8887
if !status_text.is_empty() {
89-
status_clone.set_label(&status_text);
88+
status_label.set_label(&status_text);
9089
println!("{}", status_text);
9190
}
9291

@@ -100,15 +99,13 @@ fn main() {
10099
});
101100

102101
// Also handle regular ComboBox events for completeness
103-
let status_clone2 = status_label.clone();
104102
combo_box.on_text_updated(move |_event_data| {
105-
status_clone2.set_label("Status: Text updated");
103+
status_label.set_label("Status: Text updated");
106104
});
107105

108-
let status_clone3 = status_label.clone();
109106
combo_box.on_selection_changed(move |event_data| {
110107
if let Some(selection) = event_data.get_selection() {
111-
status_clone3.set_label(&format!("Status: Selected item {}", selection));
108+
status_label.set_label(&format!("Status: Selected item {}", selection));
112109
}
113110
});
114111

@@ -132,10 +129,9 @@ fn main() {
132129
panel.set_sizer(sizer, true);
133130

134131
// Handle frame close event
135-
let frame_clone = frame.clone();
136132
frame.on_close(move |_event_data| {
137133
println!("Frame closing!");
138-
frame_clone.destroy();
134+
frame.destroy();
139135
});
140136

141137
// Show the frame

examples/rust/custom_widget/src/anim_fill_button.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,16 @@ custom_widget!(
5151
let timer = Rc::new(Timer::new(&panel));
5252

5353
// 设置绘制事件
54-
let panel_paint = panel.clone();
5554
let animation_data_paint = animation_data.clone();
5655
let config_paint = config.clone();
5756
panel.on_paint(move |event| {
5857
let animation = animation_data_paint.borrow();
59-
AniFillButton::draw_custom_button(&panel_paint, &config_paint, animation.progress, animation.is_mouse_pressed);
58+
AniFillButton::draw_custom_button(&panel, &config_paint, animation.progress, animation.is_mouse_pressed);
6059
event.skip(true);
6160
});
6261

6362
// 设置鼠标进入事件
6463
let animation_data_enter = animation_data.clone();
65-
let panel_enter = panel.clone();
6664
let timer_enter = timer.clone();
6765
panel.on_mouse_enter(move |event| {
6866
let mut animation = animation_data_enter.borrow_mut();
@@ -73,14 +71,13 @@ custom_widget!(
7371
drop(animation);
7472

7573
timer_enter.start(16, false); // 60fps
76-
panel_enter.refresh(false, None);
74+
panel.refresh(false, None);
7775
}
7876
event.skip(true);
7977
});
8078

8179
// 设置鼠标离开事件
8280
let animation_data_leave = animation_data.clone();
83-
let panel_leave = panel.clone();
8481
let timer_leave = timer.clone();
8582
panel.on_mouse_leave(move |event| {
8683
let mut animation = animation_data_leave.borrow_mut();
@@ -98,35 +95,33 @@ custom_widget!(
9895
drop(animation);
9996

10097
timer_leave.start(16, false); // 60fps
101-
panel_leave.refresh(false, None);
98+
panel.refresh(false, None);
10299
} else {
103100
drop(animation);
104-
panel_leave.refresh(false, None);
101+
panel.refresh(false, None);
105102
}
106103
event.skip(true);
107104
});
108105

109106
// 设置鼠标按下事件
110107
let animation_data_down = animation_data.clone();
111-
let panel_down = panel.clone();
112108
panel.on_mouse_left_down(move |event| {
113109
{
114110
let mut animation = animation_data_down.borrow_mut();
115111
animation.is_mouse_pressed = true;
116112
}
117-
panel_down.refresh(false, None);
113+
panel.refresh(false, None);
118114
event.skip(true);
119115
});
120116

121117
// 设置鼠标释放事件
122118
let animation_data_up = animation_data.clone();
123-
let panel_up = panel.clone();
124119
panel.on_mouse_left_up(move |event| {
125120
{
126121
let mut animation = animation_data_up.borrow_mut();
127122
animation.is_mouse_pressed = false;
128123
}
129-
panel_up.refresh(false, None);
124+
panel.refresh(false, None);
130125
event.skip(true);
131126
});
132127

@@ -137,7 +132,6 @@ custom_widget!(
137132
// 设置定时器事件
138133
let animation_data_timer = animation_data.clone();
139134
let config_timer = config.clone();
140-
let panel_timer = panel.clone();
141135
let timer_clone = timer.clone();
142136
timer.on_tick(move |event| {
143137
let mut animation = animation_data_timer.borrow_mut();
@@ -159,7 +153,7 @@ custom_widget!(
159153
AnimationState::Idle => 0.0,
160154
};
161155

162-
panel_timer.refresh(false, None);
156+
panel.refresh(false, None);
163157

164158
// 动画完成时停止
165159
if progress_ratio >= 1.0 {

examples/rust/custom_widget/src/pie_chart.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,16 @@ custom_widget!(
6666
let timer = Rc::new(Timer::new(&panel));
6767

6868
// Set up paint event
69-
let panel_paint = panel.clone();
7069
let animation_data_paint = animation_data.clone();
7170
let config_paint = config.clone();
7271
panel.on_paint(move |event| {
7372
let animation = animation_data_paint.borrow();
74-
PieChart::draw_pie_chart(&panel_paint, &config_paint, &animation);
73+
PieChart::draw_pie_chart(&panel, &config_paint, &animation);
7574
event.skip(true);
7675
});
7776

7877
// Set up mouse motion event for hover detection
7978
let animation_data_motion = animation_data.clone();
80-
let panel_motion = panel.clone();
8179
let timer_motion = timer.clone();
8280
let config_motion = config.clone();
8381
panel.on_mouse_motion(move |event| {
@@ -89,7 +87,7 @@ custom_widget!(
8987
};
9088

9189
if let Some(pos) = mouse_pos {
92-
let size = panel_motion.get_size();
90+
let size = panel.get_size();
9391
let center_x = size.width / 2;
9492
let center_y = size.height / 2;
9593

@@ -117,7 +115,7 @@ custom_widget!(
117115
timer_motion.start(16, false); // 60fps
118116
}
119117

120-
panel_motion.refresh(false, None);
118+
panel.refresh(false, None);
121119
}
122120
}
123121

@@ -126,7 +124,6 @@ custom_widget!(
126124

127125
// Set up mouse leave event
128126
let animation_data_leave = animation_data.clone();
129-
let panel_leave = panel.clone();
130127
let timer_leave = timer.clone();
131128
panel.on_mouse_leave(move |event| {
132129
let mut animation = animation_data_leave.borrow_mut();
@@ -138,7 +135,7 @@ custom_widget!(
138135
drop(animation);
139136

140137
timer_leave.start(16, false); // 60fps
141-
panel_leave.refresh(false, None);
138+
panel.refresh(false, None);
142139
}
143140

144141
event.skip(true);
@@ -147,7 +144,6 @@ custom_widget!(
147144
// Set up timer event for animations
148145
let animation_data_timer = animation_data.clone();
149146
let config_timer = config.clone();
150-
let panel_timer = panel.clone();
151147
let timer_clone = timer.clone();
152148
timer.on_tick(move |event| {
153149
let mut animation = animation_data_timer.borrow_mut();
@@ -180,7 +176,7 @@ custom_widget!(
180176
}
181177
}
182178

183-
panel_timer.refresh(false, None);
179+
panel.refresh(false, None);
184180

185181
// Animation complete
186182
if progress_ratio >= 1.0 {

examples/rust/dataviewtree/src/edit_dialog.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,12 @@ impl NodeEditDialog {
7878
);
7979

8080
// Wire default behaviors
81-
{
82-
let dlg_ok = dlg.clone();
83-
ok_btn.on_click(move |_| {
84-
dlg_ok.end_modal(ID_OK);
85-
});
86-
}
87-
{
88-
let dlg_cancel = dlg.clone();
89-
cancel_btn.on_click(move |_| {
90-
dlg_cancel.end_modal(ID_CANCEL);
91-
});
92-
}
81+
ok_btn.on_click(move |_| {
82+
dlg.end_modal(ID_OK);
83+
});
84+
cancel_btn.on_click(move |_| {
85+
dlg.end_modal(ID_CANCEL);
86+
});
9387

9488
// Disable non-applicable fields for branch nodes
9589
if is_branch {

0 commit comments

Comments
 (0)