wxdragon 0.9.15

Safe Rust bindings for wxWidgets via the wxDragon C wrapper
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
use crate::event::{Event, EventType, WxEvtHandler};
use crate::geometry::{Point, Size};
use crate::id::Id;
use crate::window::{WindowHandle, WxWidget};
// Window is used by MediaCtrlEventData for backwards compatibility
#[allow(unused_imports)]
use crate::window::Window;
use std::ffi::CString;
use wxdragon_sys as ffi;

// Define a style enum for MediaCtrl
widget_style_enum!(
    name: MediaCtrlStyle,
    doc: "Style flags for MediaCtrl widget.",
    variants: {
        NoAutoResize: ffi::WXD_MC_NO_AUTORESIZE, "Don't automatically resize the media to match the control size."
    },
    default_variant: NoAutoResize
);

widget_style_enum!(
    name: MediaState,
    doc: "State of the media player.",
    variants: {
        Stopped: ffi::WXD_MEDIASTATE_STOPPED, "Media is stopped.",
        Paused: ffi::WXD_MEDIASTATE_PAUSED, "Media is paused.",
        Playing: ffi::WXD_MEDIASTATE_PLAYING, "Media is playing."
    },
    default_variant: Stopped
);

widget_style_enum!(
    name: MediaCtrlPlayerControls,
    doc: "Player controls for the media player.",
    variants: {
        None: ffi::WXD_MEDIACTRLPLAYERCONTROLS_NONE, "No player controls.",
        Step: ffi::WXD_MEDIACTRLPLAYERCONTROLS_STEP, "Step player controls.",
        Volume: ffi::WXD_MEDIACTRLPLAYERCONTROLS_VOLUME, "Volume player controls.",
        Default: ffi::WXD_MEDIACTRLPLAYERCONTROLS_DEFAULT, "Default player controls."
    },
    default_variant: Default
);

/// Events emitted by MediaCtrl
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaCtrlEvent {
    /// Emitted when media is successfully loaded
    Loaded,
    /// Emitted when media is stopped
    Stop,
    /// Emitted when media playback has finished
    Finished,
    /// Emitted when media state changes
    StateChanged,
    /// Emitted when media starts playing
    Play,
    /// Emitted when media is paused
    Pause,
}

/// Event data for MediaCtrl events
#[derive(Debug)]
pub struct MediaCtrlEventData {
    event: Event,
}

impl MediaCtrlEventData {
    /// Create a new MediaCtrlEventData from a generic Event
    pub fn new(event: Event) -> Self {
        Self { event }
    }

    /// Get the current state of the media player
    pub fn get_state(&self) -> Option<MediaState> {
        // Since the event doesn't provide state information directly,
        // we can get the mediaCtrl from the event source and query it
        if let Some(window_obj) = self.event.get_event_object() {
            let media_ctrl = MediaCtrl {
                handle: WindowHandle::new(window_obj.as_ptr()),
            };
            return Some(media_ctrl.get_state());
        }
        None
    }
}

/// Represents a seek mode for media controls and similar use cases
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(i32)]
pub enum SeekMode {
    /// Seek from start of media (offset is positive from the beginning)
    #[default]
    FromStart = 0, // wxFromStart
    /// Seek from current position (offset can be negative or positive)
    FromCurrent = 1, // wxFromCurrent
    /// Seek from end of media (offset is usually negative from the end)
    FromEnd = 2, // wxFromEnd
}

/// A wxWidgets media player control
///
/// MediaCtrl uses `WindowHandle` internally for safe memory management.
/// When the underlying window is destroyed (by calling `destroy()` or when
/// its parent is destroyed), the handle becomes invalid and all operations
/// become safe no-ops or return default values.
#[derive(Clone, Copy)]
pub struct MediaCtrl {
    /// Safe handle to the underlying wxMediaCtrl - automatically invalidated on destroy
    handle: WindowHandle,
}

impl MediaCtrl {
    /// Creates a new `MediaCtrlBuilder` for constructing a media control.
    pub fn builder(parent: &dyn WxWidget) -> MediaCtrlBuilder<'_> {
        MediaCtrlBuilder::new(parent)
    }

    /// Helper to get raw media ctrl pointer, returns null if widget has been destroyed
    #[inline]
    fn media_ctrl_ptr(&self) -> *mut ffi::wxd_MediaCtrl_t {
        self.handle
            .get_ptr()
            .map(|p| p as *mut ffi::wxd_MediaCtrl_t)
            .unwrap_or(std::ptr::null_mut())
    }

    /// Play the media.
    /// Returns false if the widget has been destroyed.
    pub fn play(&self) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        unsafe { ffi::wxd_MediaCtrl_Play(ptr) }
    }

    /// Pause the media.
    /// Returns false if the widget has been destroyed.
    pub fn pause(&self) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        unsafe { ffi::wxd_MediaCtrl_Pause(ptr) }
    }

    /// Stop the media.
    /// Returns false if the widget has been destroyed.
    pub fn stop(&self) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        unsafe { ffi::wxd_MediaCtrl_Stop(ptr) }
    }

    /// Load media from a file path.
    /// Returns false if the widget has been destroyed.
    pub fn load(&self, file_name: &str) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        let c_file_name = CString::new(file_name).expect("CString::new failed for file_name");
        unsafe { ffi::wxd_MediaCtrl_Load(ptr, c_file_name.as_ptr()) }
    }

    /// Load media from a URI.
    /// Returns false if the widget has been destroyed.
    pub fn load_uri(&self, uri: &str) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        let c_uri = CString::new(uri).expect("CString::new failed for uri");
        unsafe { ffi::wxd_MediaCtrl_LoadURI(ptr, c_uri.as_ptr()) }
    }

    /// Load media from a URI using a proxy.
    /// Returns false if the widget has been destroyed.
    pub fn load_uri_with_proxy(&self, uri: &str, proxy: &str) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        let c_uri = CString::new(uri).expect("CString::new failed for uri");
        let c_proxy = CString::new(proxy).expect("CString::new failed for proxy");
        unsafe { ffi::wxd_MediaCtrl_LoadURIWithProxy(ptr, c_uri.as_ptr(), c_proxy.as_ptr()) }
    }

    /// Get the current state of the media.
    /// Returns Stopped if the widget has been destroyed.
    pub fn get_state(&self) -> MediaState {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return MediaState::Stopped;
        }
        let state = unsafe { ffi::wxd_MediaCtrl_GetState(ptr) };

        match state as u32 {
            0 => MediaState::Stopped,
            1 => MediaState::Paused,
            2 => MediaState::Playing,
            _ => MediaState::Stopped, // Default to Stopped for unknown values
        }
    }

    /// Seek to a position in the media.
    /// Returns 0 if the widget has been destroyed.
    pub fn seek(&self, where_: i64, mode: SeekMode) -> i64 {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return 0;
        }
        unsafe { ffi::wxd_MediaCtrl_Seek(ptr, where_, mode as i32) }
    }

    /// Get the current position in the media.
    /// Returns 0 if the widget has been destroyed.
    pub fn tell(&self) -> i64 {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return 0;
        }
        unsafe { ffi::wxd_MediaCtrl_Tell(ptr) }
    }

    /// Get the length of the media.
    /// Returns 0 if the widget has been destroyed.
    pub fn length(&self) -> i64 {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return 0;
        }
        unsafe { ffi::wxd_MediaCtrl_Length(ptr) }
    }

    /// Get the current playback rate.
    /// Returns 0.0 if the widget has been destroyed.
    pub fn get_playback_rate(&self) -> f64 {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return 0.0;
        }
        unsafe { ffi::wxd_MediaCtrl_GetPlaybackRate(ptr) }
    }

    /// Set the playback rate.
    /// Returns false if the widget has been destroyed.
    pub fn set_playback_rate(&self, rate: f64) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        unsafe { ffi::wxd_MediaCtrl_SetPlaybackRate(ptr, rate) }
    }

    /// Get the download progress (DirectShow only).
    /// Returns 0 if the widget has been destroyed.
    pub fn get_download_progress(&self) -> i64 {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return 0;
        }
        unsafe { ffi::wxd_MediaCtrl_GetDownloadProgress(ptr) }
    }

    /// Get the download total (DirectShow only).
    /// Returns 0 if the widget has been destroyed.
    pub fn get_download_total(&self) -> i64 {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return 0;
        }
        unsafe { ffi::wxd_MediaCtrl_GetDownloadTotal(ptr) }
    }

    /// Get the current volume.
    /// Returns 0.0 if the widget has been destroyed.
    pub fn get_volume(&self) -> f64 {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return 0.0;
        }
        unsafe { ffi::wxd_MediaCtrl_GetVolume(ptr) }
    }

    /// Set the volume.
    /// Returns false if the widget has been destroyed.
    pub fn set_volume(&self, volume: f64) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        unsafe { ffi::wxd_MediaCtrl_SetVolume(ptr, volume) }
    }

    /// Show player controls.
    /// Returns false if the widget has been destroyed.
    pub fn show_player_controls(&self, controls: MediaCtrlPlayerControls) -> bool {
        let ptr = self.media_ctrl_ptr();
        if ptr.is_null() {
            return false;
        }
        unsafe { ffi::wxd_MediaCtrl_ShowPlayerControls(ptr, controls.bits() as u32 as i32) }
    }

    /// Returns the underlying WindowHandle for this media control.
    pub fn window_handle(&self) -> WindowHandle {
        self.handle
    }
}

// Manual WxWidget implementation for MediaCtrl (using WindowHandle)
impl WxWidget for MediaCtrl {
    fn handle_ptr(&self) -> *mut ffi::wxd_Window_t {
        self.handle.get_ptr().unwrap_or(std::ptr::null_mut())
    }

    fn is_valid(&self) -> bool {
        self.handle.is_valid()
    }
}

// Implement WxEvtHandler for event binding
impl WxEvtHandler for MediaCtrl {
    unsafe fn get_event_handler_ptr(&self) -> *mut ffi::wxd_EvtHandler_t {
        self.handle.get_ptr().unwrap_or(std::ptr::null_mut()) as *mut ffi::wxd_EvtHandler_t
    }
}

// Implement common event traits that all Window-based widgets support
impl crate::event::WindowEvents for MediaCtrl {}

// Implement event handlers for MediaCtrl
crate::implement_widget_local_event_handlers!(
    MediaCtrl,
    MediaCtrlEvent,
    MediaCtrlEventData,
    Loaded => loaded, EventType::MEDIA_LOADED,
    Stop => stop, EventType::MEDIA_STOP,
    Finished => finished, EventType::MEDIA_FINISHED,
    StateChanged => state_changed, EventType::MEDIA_STATECHANGED,
    Play => play, EventType::MEDIA_PLAY,
    Pause => pause, EventType::MEDIA_PAUSE
);

// XRC Support - enables MediaCtrl to be created from XRC-managed pointers
#[cfg(feature = "xrc")]
impl crate::xrc::XrcSupport for MediaCtrl {
    unsafe fn from_xrc_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
        MediaCtrl {
            handle: WindowHandle::new(ptr),
        }
    }
}

// Create the builder for MediaCtrl
widget_builder!(
    name: MediaCtrl,
    parent_type: &'a dyn WxWidget,
    style_type: MediaCtrlStyle,
    fields: {
        file_name: String = String::new(),
        backend_name: String = String::new()
    },
    build_impl: |slf| {
        let parent_ptr = slf.parent.handle_ptr();
        let c_file_name = CString::new(slf.file_name.clone()).unwrap();
        let c_backend_name = CString::new(slf.backend_name.clone()).unwrap();

        let ptr = unsafe {
            ffi::wxd_MediaCtrl_Create(
                parent_ptr,
                slf.id,
                c_file_name.as_ptr(),
                slf.pos.x, slf.pos.y,
                slf.size.width, slf.size.height,
                slf.style.bits(),
                c_backend_name.as_ptr(),
            )
        };

        assert!(!ptr.is_null(), "Failed to create MediaCtrl");

        MediaCtrl {
            handle: WindowHandle::new(ptr as *mut ffi::wxd_Window_t),
        }
    }
);

// Enable widget casting for MediaCtrl
impl crate::window::FromWindowWithClassName for MediaCtrl {
    fn class_name() -> &'static str {
        "wxMediaCtrl"
    }

    unsafe fn from_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
        MediaCtrl {
            handle: WindowHandle::new(ptr),
        }
    }
}