Skip to main content

wxdragon/event/
mod.rs

1//! Safe wrappers for wxWidgets events.
2
3use crate::geometry::Point;
4use crate::window::Window;
5use std::boxed::Box;
6use std::ffi::CStr;
7use std::ffi::c_void;
8use wxdragon_sys as ffi;
9pub mod app_events;
10pub mod button_events;
11pub mod event_data;
12pub mod macros;
13pub mod menu_events;
14pub mod scroll_events;
15pub mod taskbar_events;
16pub mod text_events;
17pub mod tree_events;
18#[cfg(feature = "webview")]
19pub mod webview_events;
20pub mod window_events;
21
22// Re-export window events for easier access
23pub use window_events::{
24    IdleEventData, KeyboardEvent, MouseButtonEvent, MouseEnterEvent, MouseLeaveEvent, MouseMotionEvent, WindowEvent,
25    WindowEventData, WindowEvents, WindowSizeEvent,
26};
27
28// Re-export button events for easier access
29pub use button_events::{ButtonEvent, ButtonEventData, ButtonEvents};
30
31// Re-export text events for easier access
32pub use text_events::{TextEvent, TextEventData, TextEvents};
33
34// Re-export tree events for easier access
35pub use tree_events::{TreeEvent, TreeEventData, TreeEvents};
36
37// Re-export scroll events for easier access
38pub use scroll_events::{ScrollEvent, ScrollEventType, ScrollEvents};
39
40// Re-export webview events for easier access
41#[cfg(feature = "webview")]
42pub use webview_events::{WebViewEvent, WebViewEventData, WebViewEvents};
43
44// Re-export menu events for easier access
45pub use menu_events::{MenuEvent, MenuEventData, MenuEvents};
46
47// Re-export taskbar events for easier access
48#[cfg(any(target_os = "windows", target_os = "linux"))]
49pub use taskbar_events::{TaskBarIconEvent, TaskBarIconEventData};
50
51// Re-export app events for easier access
52pub use app_events::AppEvents;
53
54// Re-export the stable C enum for use in the safe wrapper
55pub use ffi::WXDEventTypeCEnum;
56
57// --- EventToken ---
58
59/// Unique identifier for an event binding.
60///
61/// An `EventToken` is returned when binding an event handler and can be used to
62/// later unbind that specific handler. Tokens are opaque, unique, and thread-safe.
63///
64/// # Example
65///
66/// ```ignore
67/// let token = button.on_click(|_| println!("clicked"));
68/// // ... later ...
69/// button.unbind(token);  // Unbind this specific handler
70/// ```
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72pub struct EventToken(usize);
73
74impl From<usize> for EventToken {
75    fn from(value: usize) -> Self {
76        EventToken(value)
77    }
78}
79
80impl From<EventToken> for usize {
81    fn from(token: EventToken) -> Self {
82        token.0
83    }
84}
85
86impl EventToken {
87    /// Check if this token is valid (non-zero)
88    pub fn is_valid(&self) -> bool {
89        *self != Self::INVALID_TOKEN
90    }
91
92    /// Constant representing an invalid/null token
93    pub const INVALID_TOKEN: EventToken = EventToken(0);
94}
95
96// --- EventType Enum ---
97
98bitflags::bitflags! {
99/// Represents a wxDragon event type using stable C enum values.
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101#[repr(transparent)] // Ensures memory layout matches the underlying C enum integer type
102pub struct EventType: ffi::WXDEventTypeCEnum { // Use the generated C enum type
103    // Constants map directly to the stable C enum values
104    const COMMAND_BUTTON_CLICKED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_BUTTON_CLICKED;
105    const CLOSE_WINDOW = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CLOSE_WINDOW;
106    const CHECKBOX = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CHECKBOX;
107    const TEXT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TEXT;
108    const TEXT_ENTER = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TEXT_ENTER;
109    const SIZE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SIZE;
110    const MENU = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MENU;
111    // NEW: Menu event types
112    const MENU_OPEN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MENU_OPEN;
113    const MENU_CLOSE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MENU_CLOSE;
114    const MENU_HIGHLIGHT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MENU_HIGHLIGHT;
115    const CONTEXT_MENU = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CONTEXT_MENU;
116    const LEFT_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LEFT_DOWN;
117    const LEFT_UP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LEFT_UP;
118    const RIGHT_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RIGHT_DOWN;
119    const RIGHT_UP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RIGHT_UP;
120    const MIDDLE_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MIDDLE_DOWN;
121    const MIDDLE_UP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MIDDLE_UP;
122    const MOTION = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MOTION;
123    const MOUSEWHEEL = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MOUSEWHEEL;
124    const ENTER_WINDOW = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_ENTER_WINDOW;
125    const LEAVE_WINDOW = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LEAVE_WINDOW;
126    const KEY_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_KEY_DOWN;
127    const KEY_UP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_KEY_UP;
128    const CHAR = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CHAR;
129    const COMMAND_RADIOBUTTON_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_RADIOBUTTON_SELECTED;
130    const COMMAND_RADIOBOX_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_RADIOBOX_SELECTED;
131    const COMMAND_LISTBOX_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_LISTBOX_SELECTED;
132    const COMMAND_CHOICE_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_CHOICE_SELECTED;
133    const COMMAND_COMBOBOX_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_COMBOBOX_SELECTED;
134    const COMMAND_CHECKLISTBOX_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_CHECKLISTBOX_SELECTED;
135    const COMMAND_LISTBOX_DOUBLECLICKED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_LISTBOX_DOUBLECLICKED;
136    const COMMAND_TOGGLEBUTTON_CLICKED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_TOGGLEBUTTON_CLICKED;
137    // ADDED: RearrangeList event type
138    const COMMAND_REARRANGE_LIST = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_REARRANGE_LIST;
139    // ADDED: CollapsiblePane event type
140    const COLLAPSIBLEPANE_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COLLAPSIBLEPANE_CHANGED;
141    // ADDED: TreeCtrl event types
142    const TREE_BEGIN_LABEL_EDIT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_BEGIN_LABEL_EDIT;
143    const TREE_END_LABEL_EDIT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_END_LABEL_EDIT;
144    const TREE_SEL_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_SEL_CHANGED;
145    const TREE_ITEM_ACTIVATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_ACTIVATED;
146    // ADDED: TreeListCtrl event types
147    const TREELIST_SELECTION_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREELIST_SELECTION_CHANGED;
148    const TREELIST_ITEM_CHECKED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREELIST_ITEM_CHECKED;
149    const TREELIST_ITEM_ACTIVATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREELIST_ITEM_ACTIVATED;
150    const TREELIST_COLUMN_SORTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREELIST_COLUMN_SORTED;
151    const TREELIST_ITEM_EXPANDING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREELIST_ITEM_EXPANDING;
152    const TREELIST_ITEM_EXPANDED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREELIST_ITEM_EXPANDED;
153    // ADDED: Slider event type
154    const SLIDER = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SLIDER;
155    // ADDED: SpinCtrl event type
156    const SPINCTRL = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPINCTRL;
157    // ADDED: SpinButton event types
158    const SPIN_UP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPIN_UP;
159    const SPIN_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPIN_DOWN;
160    const SPIN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPIN;
161    // ADDED: Notebook event type
162    const NOTEBOOK_PAGE_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_NOTEBOOK_PAGE_CHANGED;
163    // ADDED: Splitter event types
164    const SPLITTER_SASH_POS_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPLITTER_SASH_POS_CHANGED;
165    const SPLITTER_SASH_POS_CHANGING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPLITTER_SASH_POS_CHANGING;
166    const SPLITTER_DOUBLECLICKED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPLITTER_DOUBLECLICKED;
167    const SPLITTER_UNSPLIT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPLITTER_UNSPLIT;
168    // ADDED: ListCtrl event types
169    const LIST_ITEM_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_ITEM_SELECTED;
170    const LIST_ITEM_ACTIVATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_ITEM_ACTIVATED;
171    const LIST_COL_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_COL_CLICK;
172    const LIST_BEGIN_LABEL_EDIT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_BEGIN_LABEL_EDIT;
173    const LIST_END_LABEL_EDIT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_END_LABEL_EDIT;
174    // ADDED: Additional ListCtrl event types
175    const LIST_BEGIN_DRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_BEGIN_DRAG;
176    const LIST_BEGIN_RDRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_BEGIN_RDRAG;
177    const LIST_DELETE_ITEM = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_DELETE_ITEM;
178    const LIST_DELETE_ALL_ITEMS = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_DELETE_ALL_ITEMS;
179    const LIST_ITEM_DESELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_ITEM_DESELECTED;
180    const LIST_ITEM_FOCUSED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_ITEM_FOCUSED;
181    const LIST_ITEM_MIDDLE_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_ITEM_MIDDLE_CLICK;
182    const LIST_ITEM_RIGHT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_ITEM_RIGHT_CLICK;
183    const LIST_KEY_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_KEY_DOWN;
184    const LIST_INSERT_ITEM = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_INSERT_ITEM;
185    const LIST_COL_RIGHT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_COL_RIGHT_CLICK;
186    const LIST_COL_BEGIN_DRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_LIST_COL_BEGIN_DRAG;
187    // ADDED: ColourPickerCtrl event type
188    const COLOURPICKER_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COLOURPICKER_CHANGED;
189    // DatePicker Event
190    const DATE_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATE_CHANGED;
191    // TimePicker Event
192    const TIME_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TIME_CHANGED;
193    // Treebook Events (match WXDEventTypeCEnum values)
194    const TREEBOOK_PAGE_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREEBOOK_PAGE_CHANGED;
195    const TREEBOOK_PAGE_CHANGING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREEBOOK_PAGE_CHANGING;
196    const TREEBOOK_NODE_EXPANDED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREEBOOK_NODE_EXPANDED;
197    const TREEBOOK_NODE_COLLAPSED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREEBOOK_NODE_COLLAPSED;
198    // ADDED: SearchCtrl Event Types
199    const COMMAND_SEARCHCTRL_SEARCH_BTN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_SEARCHCTRL_SEARCH_BTN;
200    const COMMAND_SEARCHCTRL_CANCEL_BTN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_SEARCHCTRL_CANCEL_BTN;
201    const COMMAND_HYPERLINK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_COMMAND_HYPERLINK;
202    const SPINCTRLDOUBLE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SPINCTRLDOUBLE;
203    // ADDED: Calendar Control Event Type
204    const CALENDAR_SEL_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CALENDAR_SEL_CHANGED;
205    // ADDED: Missing Calendar Control Event Types
206    const CALENDAR_DOUBLECLICKED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CALENDAR_DOUBLECLICKED;
207    const CALENDAR_MONTH_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CALENDAR_MONTH_CHANGED;
208    const CALENDAR_YEAR_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CALENDAR_YEAR_CHANGED;
209    const CALENDAR_WEEKDAY_CLICKED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_CALENDAR_WEEKDAY_CLICKED;
210    // ADDED: ScrollBar Events
211    const SCROLL_TOP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_TOP;
212    const SCROLL_BOTTOM = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_BOTTOM;
213    const SCROLL_LINEUP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_LINEUP;
214    const SCROLL_LINEDOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_LINEDOWN;
215    const SCROLL_PAGEUP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_PAGEUP;
216    const SCROLL_PAGEDOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_PAGEDOWN;
217    const SCROLL_THUMBTRACK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_THUMBTRACK;
218    const SCROLL_THUMBRELEASE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_THUMBRELEASE;
219    const SCROLL_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SCROLL_CHANGED;
220    const FILE_PICKER_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_FILEPICKER_CHANGED;
221    const DIR_PICKER_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DIRPICKER_CHANGED;
222    const FONT_PICKER_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_FONTPICKER_CHANGED;
223
224    const NOTIFICATION_MESSAGE_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_NOTIFICATION_MESSAGE_CLICK;
225    const NOTIFICATION_MESSAGE_DISMISSED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_NOTIFICATION_MESSAGE_DISMISSED;
226    const NOTIFICATION_MESSAGE_ACTION = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_NOTIFICATION_MESSAGE_ACTION;
227
228    // Media events - only available when media-ctrl feature is enabled
229    #[cfg(feature = "media-ctrl")]
230    const MEDIA_LOADED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MEDIA_LOADED;
231    #[cfg(feature = "media-ctrl")]
232    const MEDIA_STOP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MEDIA_STOP;
233    #[cfg(feature = "media-ctrl")]
234    const MEDIA_FINISHED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MEDIA_FINISHED;
235    #[cfg(feature = "media-ctrl")]
236    const MEDIA_STATECHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MEDIA_STATECHANGED;
237    #[cfg(feature = "media-ctrl")]
238    const MEDIA_PLAY = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MEDIA_PLAY;
239    #[cfg(feature = "media-ctrl")]
240    const MEDIA_PAUSE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MEDIA_PAUSE;
241
242    const EVT_DATE_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATE_CHANGED;
243
244    const IDLE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_IDLE;
245
246    // Drag and drop events
247    const DROP_FILES = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DROP_FILES;
248
249    const PAINT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_PAINT;
250
251    const DESTROY = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DESTROY;
252
253    // Additional window events
254    const MOVE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_MOVE;
255    const ERASE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_ERASE;
256    const SET_FOCUS = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_SET_FOCUS;
257    const KILL_FOCUS = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_KILL_FOCUS;
258    const ACTIVATE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_ACTIVATE;
259
260    // DataView events
261    const DATAVIEW_SELECTION_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_SELECTION_CHANGED;
262    const DATAVIEW_ITEM_ACTIVATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_ACTIVATED;
263    const DATAVIEW_ITEM_EDITING_STARTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_EDITING_STARTED;
264    const DATAVIEW_ITEM_EDITING_DONE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_EDITING_DONE;
265    const DATAVIEW_ITEM_COLLAPSING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_COLLAPSING;
266    const DATAVIEW_ITEM_COLLAPSED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_COLLAPSED;
267    const DATAVIEW_ITEM_EXPANDING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_EXPANDING;
268    const DATAVIEW_ITEM_EXPANDED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_EXPANDED;
269    const DATAVIEW_COLUMN_HEADER_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_COLUMN_HEADER_CLICK;
270    const DATAVIEW_COLUMN_HEADER_RIGHT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
271    const DATAVIEW_COLUMN_SORTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_COLUMN_SORTED;
272    const DATAVIEW_COLUMN_REORDERED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_COLUMN_REORDERED;
273    const DATAVIEW_ITEM_CONTEXT_MENU = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_DATAVIEW_ITEM_CONTEXT_MENU;
274
275    // ADDED: New TreeCtrl Event Types (complementing 22-25)
276    const TREE_SEL_CHANGING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_SEL_CHANGING;
277    const TREE_ITEM_COLLAPSING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_COLLAPSING;
278    const TREE_ITEM_COLLAPSED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_COLLAPSED;
279    const TREE_ITEM_EXPANDING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_EXPANDING;
280    const TREE_ITEM_EXPANDED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_EXPANDED;
281    const TREE_ITEM_RIGHT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_RIGHT_CLICK;
282    const TREE_ITEM_MIDDLE_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_MIDDLE_CLICK;
283    const TREE_KEY_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_KEY_DOWN;
284    const TREE_DELETE_ITEM = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_DELETE_ITEM;
285    const TREE_ITEM_MENU = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_MENU;
286    const TREE_BEGIN_DRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_BEGIN_DRAG;
287    const TREE_BEGIN_RDRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_BEGIN_RDRAG;
288    const TREE_END_DRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_END_DRAG;
289    const TREE_STATE_IMAGE_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_STATE_IMAGE_CLICK;
290
291    // ToolBar Events
292    const TOOL = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TOOL;
293    const TOOL_ENTER = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TOOL_ENTER;
294
295    // TreeCtrl Events
296    const TREE_ITEM_GETTOOLTIP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TREE_ITEM_GETTOOLTIP;
297
298    // Generic events that might not fit a specific category or are widely used
299    const ANY = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_ANY;
300
301    // Just a placeholder for bindgen to ensure the enum is c_int type, not c_uint instead.
302    const INVALID = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_INVALID;
303
304    // Special event type for null/None, not a real wxWidgets event type
305    const NONE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_NULL; // Assuming NULL is 0
306
307    // AuiManager events
308    #[cfg(feature = "aui")]
309    const AUI_PANE_BUTTON = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_AUI_PANE_BUTTON;
310    #[cfg(feature = "aui")]
311    const AUI_PANE_CLOSE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_AUI_PANE_CLOSE;
312    #[cfg(feature = "aui")]
313    const AUI_PANE_MAXIMIZE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_AUI_PANE_MAXIMIZE;
314    #[cfg(feature = "aui")]
315    const AUI_PANE_RESTORE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_AUI_PANE_RESTORE;
316    #[cfg(feature = "aui")]
317    const AUI_PANE_ACTIVATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_AUI_PANE_ACTIVATED;
318    #[cfg(feature = "aui")]
319    const AUI_RENDER = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_AUI_RENDER;
320
321    // Timer event
322    const TIMER = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TIMER;
323
324    // StyledTextCtrl events - only available when stc feature is enabled
325    #[cfg(feature = "stc")]
326    const STC_CHANGE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_CHANGE;
327    #[cfg(feature = "stc")]
328    const STC_STYLENEEDED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_STYLENEEDED;
329    #[cfg(feature = "stc")]
330    const STC_CHARADDED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_CHARADDED;
331    #[cfg(feature = "stc")]
332    const STC_SAVEPOINTREACHED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_SAVEPOINTREACHED;
333    #[cfg(feature = "stc")]
334    const STC_SAVEPOINTLEFT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_SAVEPOINTLEFT;
335    #[cfg(feature = "stc")]
336    const STC_ROMODIFYATTEMPT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_ROMODIFYATTEMPT;
337    #[cfg(feature = "stc")]
338    const STC_DOUBLECLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_DOUBLECLICK;
339    #[cfg(feature = "stc")]
340    const STC_UPDATEUI = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_UPDATEUI;
341    #[cfg(feature = "stc")]
342    const STC_MODIFIED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_MODIFIED;
343    #[cfg(feature = "stc")]
344    const STC_MACRORECORD = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_MACRORECORD;
345    #[cfg(feature = "stc")]
346    const STC_MARGINCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_MARGINCLICK;
347    #[cfg(feature = "stc")]
348    const STC_NEEDSHOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_NEEDSHOWN;
349    #[cfg(feature = "stc")]
350    const STC_PAINTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_PAINTED;
351    #[cfg(feature = "stc")]
352    const STC_USERLISTSELECTION = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_USERLISTSELECTION;
353    #[cfg(feature = "stc")]
354    const STC_DWELLSTART = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_DWELLSTART;
355    #[cfg(feature = "stc")]
356    const STC_DWELLEND = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_DWELLEND;
357    #[cfg(feature = "stc")]
358    const STC_START_DRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_START_DRAG;
359    #[cfg(feature = "stc")]
360    const STC_DRAG_OVER = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_DRAG_OVER;
361    #[cfg(feature = "stc")]
362    const STC_DO_DROP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_DO_DROP;
363    #[cfg(feature = "stc")]
364    const STC_ZOOM = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_ZOOM;
365    #[cfg(feature = "stc")]
366    const STC_HOTSPOT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_HOTSPOT_CLICK;
367    #[cfg(feature = "stc")]
368    const STC_HOTSPOT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_HOTSPOT_DCLICK;
369    #[cfg(feature = "stc")]
370    const STC_CALLTIP_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_CALLTIP_CLICK;
371    #[cfg(feature = "stc")]
372    const STC_AUTOCOMP_SELECTION = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_AUTOCOMP_SELECTION;
373    #[cfg(feature = "stc")]
374    const STC_INDICATOR_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_INDICATOR_CLICK;
375    #[cfg(feature = "stc")]
376    const STC_INDICATOR_RELEASE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_INDICATOR_RELEASE;
377    #[cfg(feature = "stc")]
378    const STC_AUTOCOMP_CANCELLED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_AUTOCOMP_CANCELLED;
379    #[cfg(feature = "stc")]
380    const STC_AUTOCOMP_CHAR_DELETED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_STC_AUTOCOMP_CHAR_DELETED;
381
382    // RichText events
383    #[cfg(feature = "richtext")]
384    const RICHTEXT_LEFT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_LEFT_CLICK;
385    #[cfg(feature = "richtext")]
386    const RICHTEXT_RIGHT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_RIGHT_CLICK;
387    #[cfg(feature = "richtext")]
388    const RICHTEXT_MIDDLE_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_MIDDLE_CLICK;
389    #[cfg(feature = "richtext")]
390    const RICHTEXT_LEFT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_LEFT_DCLICK;
391    #[cfg(feature = "richtext")]
392    const RICHTEXT_RETURN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_RETURN;
393    #[cfg(feature = "richtext")]
394    const RICHTEXT_CHARACTER = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_CHARACTER;
395    #[cfg(feature = "richtext")]
396    const RICHTEXT_DELETE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_DELETE;
397    #[cfg(feature = "richtext")]
398    const RICHTEXT_CONTENT_INSERTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_CONTENT_INSERTED;
399    #[cfg(feature = "richtext")]
400    const RICHTEXT_CONTENT_DELETED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_CONTENT_DELETED;
401    #[cfg(feature = "richtext")]
402    const RICHTEXT_STYLE_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_STYLE_CHANGED;
403    #[cfg(feature = "richtext")]
404    const RICHTEXT_SELECTION_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_SELECTION_CHANGED;
405    #[cfg(feature = "richtext")]
406    const RICHTEXT_STYLESHEET_CHANGING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_STYLESHEET_CHANGING;
407    #[cfg(feature = "richtext")]
408    const RICHTEXT_STYLESHEET_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_STYLESHEET_CHANGED;
409    #[cfg(feature = "richtext")]
410    const RICHTEXT_STYLESHEET_REPLACING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_STYLESHEET_REPLACING;
411    #[cfg(feature = "richtext")]
412    const RICHTEXT_STYLESHEET_REPLACED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_RICHTEXT_STYLESHEET_REPLACED;
413
414    // WebView event types - only available when webview feature is enabled
415    #[cfg(feature = "webview")]
416    const WEBVIEW_CREATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_CREATED;
417    #[cfg(feature = "webview")]
418    const WEBVIEW_NAVIGATING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_NAVIGATING;
419    #[cfg(feature = "webview")]
420    const WEBVIEW_NAVIGATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_NAVIGATED;
421    #[cfg(feature = "webview")]
422    const WEBVIEW_LOADED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_LOADED;
423    #[cfg(feature = "webview")]
424    const WEBVIEW_ERROR = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_ERROR;
425    #[cfg(feature = "webview")]
426    const WEBVIEW_NEWWINDOW = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_NEWWINDOW;
427    #[cfg(feature = "webview")]
428    const WEBVIEW_NEWWINDOW_FEATURES = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_NEWWINDOW_FEATURES;
429    #[cfg(feature = "webview")]
430    const WEBVIEW_TITLE_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_TITLE_CHANGED;
431    #[cfg(feature = "webview")]
432    const WEBVIEW_FULLSCREEN_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_FULLSCREEN_CHANGED;
433    #[cfg(feature = "webview")]
434    const WEBVIEW_SCRIPT_MESSAGE_RECEIVED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_SCRIPT_MESSAGE_RECEIVED;
435    #[cfg(feature = "webview")]
436    const WEBVIEW_SCRIPT_RESULT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_SCRIPT_RESULT;
437    #[cfg(feature = "webview")]
438    const WEBVIEW_WINDOW_CLOSE_REQUESTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_WINDOW_CLOSE_REQUESTED;
439    #[cfg(feature = "webview")]
440    const WEBVIEW_BROWSING_DATA_CLEARED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_WEBVIEW_BROWSING_DATA_CLEARED;
441
442    // TaskBarIcon Event Types - platform-specific support
443
444    // Taskbar event identifiers exposed by the generated bindings.
445    // Runtime support is still platform-specific; Linux only exposes a subset
446    // of these as native tray events.
447    const TASKBAR_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_CLICK;
448    const TASKBAR_LEFT_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_LEFT_DOWN;
449    const TASKBAR_LEFT_UP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_LEFT_UP;
450    const TASKBAR_RIGHT_DOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_RIGHT_DOWN;
451    const TASKBAR_RIGHT_UP = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_RIGHT_UP;
452    const TASKBAR_LEFT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_LEFT_DCLICK;
453    const TASKBAR_RIGHT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_RIGHT_DCLICK;
454
455    // Windows-only events
456    #[cfg(target_os = "windows")]
457    const TASKBAR_MOVE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_MOVE;
458    #[cfg(target_os = "windows")]
459    const TASKBAR_BALLOON_TIMEOUT = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_BALLOON_TIMEOUT;
460    #[cfg(target_os = "windows")]
461    const TASKBAR_BALLOON_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_TASKBAR_BALLOON_CLICK;
462
463    // Grid event types
464    const GRID_CELL_LEFT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_CELL_LEFT_CLICK;
465    const GRID_CELL_RIGHT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_CELL_RIGHT_CLICK;
466    const GRID_CELL_LEFT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_CELL_LEFT_DCLICK;
467    const GRID_CELL_RIGHT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_CELL_RIGHT_DCLICK;
468    const GRID_LABEL_LEFT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_LABEL_LEFT_CLICK;
469    const GRID_LABEL_RIGHT_CLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_LABEL_RIGHT_CLICK;
470    const GRID_LABEL_LEFT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_LABEL_LEFT_DCLICK;
471    const GRID_LABEL_RIGHT_DCLICK = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_LABEL_RIGHT_DCLICK;
472    const GRID_CELL_CHANGED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_CELL_CHANGED;
473    const GRID_SELECT_CELL = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_SELECT_CELL;
474    const GRID_EDITOR_SHOWN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_EDITOR_SHOWN;
475    const GRID_EDITOR_HIDDEN = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_EDITOR_HIDDEN;
476    const GRID_EDITOR_CREATED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_EDITOR_CREATED;
477    const GRID_CELL_BEGIN_DRAG = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_CELL_BEGIN_DRAG;
478    const GRID_ROW_SIZE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_ROW_SIZE;
479    const GRID_COL_SIZE = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_COL_SIZE;
480    const GRID_RANGE_SELECTED = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_RANGE_SELECTED;
481    const GRID_TABBING = ffi::WXDEventTypeCEnum_WXD_EVENT_TYPE_GRID_TABBING;
482}
483}
484
485impl EventType {
486    fn is_recognized(self) -> bool {
487        use bitflags::Flags;
488        self.iter_equal_names().next().is_some() && self != EventType::NONE && self != EventType::INVALID
489    }
490}
491
492/// Idle event processing modes
493#[derive(Debug, Clone, Copy, PartialEq, Eq)]
494pub enum IdleMode {
495    /// Send idle events to all windows
496    ProcessAll = 0,
497    /// Send idle events only to windows that explicitly request them
498    ProcessSpecified = 1,
499}
500
501/// Static methods for controlling idle event behavior
502pub struct IdleEvent;
503
504impl IdleEvent {
505    /// Sets how wxWidgets will send idle events.
506    ///
507    /// # Arguments
508    /// * `mode` - The idle processing mode
509    ///
510    /// # Example
511    /// ```ignore
512    /// use wxdragon::event::{IdleEvent, IdleMode};
513    ///
514    /// // Only send idle events to windows that request them
515    /// IdleEvent::set_mode(IdleMode::ProcessSpecified);
516    /// ```
517    pub fn set_mode(mode: IdleMode) {
518        unsafe {
519            ffi::wxd_IdleEvent_SetMode(mode as i32);
520        }
521    }
522
523    /// Gets the current idle event processing mode.
524    pub fn get_mode() -> IdleMode {
525        let mode = unsafe { ffi::wxd_IdleEvent_GetMode() };
526        match mode {
527            1 => IdleMode::ProcessSpecified,
528            _ => IdleMode::ProcessAll,
529        }
530    }
531}
532
533// --- Simple Event Struct ---
534
535/// Represents a wxWidgets event.
536/// This struct is a lightweight wrapper around the raw `wxd_Event_t*` pointer.
537/// It provides safe methods to access event details.
538#[derive(Debug, Clone, Copy)] // Raw pointers are Copy
539pub struct Event(pub(crate) *mut ffi::wxd_Event_t);
540
541impl Event {
542    /// Creates a new Event wrapper from a raw pointer.
543    /// # Safety
544    /// The pointer must be a valid `wxd_Event_t` pointer obtained from wxWidgets.
545    pub(crate) unsafe fn from_ptr(ptr: *mut ffi::wxd_Event_t) -> Self {
546        Event(ptr)
547    }
548
549    /// Gets the raw pointer to the underlying wxWidgets event object.
550    pub(crate) fn _as_ptr(&self) -> *mut ffi::wxd_Event_t {
551        self.0
552    }
553
554    /// Checks if the underlying pointer is null.
555    pub fn is_null(&self) -> bool {
556        self.0.is_null()
557    }
558
559    /// Gets the ID of the event.
560    pub fn get_id(&self) -> i32 {
561        if self.0.is_null() {
562            return ffi::WXD_ID_ANY as i32;
563        }
564        unsafe { ffi::wxd_Event_GetId(self.0) }
565    }
566
567    /// Gets the object (usually a window) that generated the event.
568    pub fn get_event_object(&self) -> Option<Window> {
569        if self.0.is_null() {
570            return None;
571        }
572        let ptr = unsafe { ffi::wxd_Event_GetEventObject(self.0) };
573        if ptr.is_null() {
574            None
575        } else {
576            Some(unsafe { Window::from_ptr(ptr) })
577        }
578    }
579
580    /// Gets the event type.
581    pub fn get_event_type(&self) -> Option<EventType> {
582        if self.0.is_null() {
583            return None;
584        }
585        #[allow(clippy::useless_conversion)]
586        let event_type_c = unsafe { ffi::wxd_Event_GetEventType(self.0) } as WXDEventTypeCEnum;
587        let evt = EventType::from_bits_retain(event_type_c);
588        if !evt.is_recognized() {
589            return None;
590        }
591        Some(evt)
592    }
593
594    /// Controls whether the event is processed further.
595    pub fn skip(&self, skip: bool) {
596        if self.0.is_null() {
597            return;
598        }
599        unsafe { ffi::wxd_Event_Skip(self.0, skip) };
600    }
601
602    // --- Common Event Data Accessors ---
603
604    /// Gets the string associated with a command event.
605    pub fn get_string(&self) -> Option<String> {
606        if self.0.is_null() {
607            return None;
608        }
609        let len = unsafe { ffi::wxd_CommandEvent_GetString(self.0, std::ptr::null_mut(), 0) };
610        if len < 0 {
611            return None;
612        }
613        let mut buf = vec![0; len as usize + 1];
614        unsafe { ffi::wxd_CommandEvent_GetString(self.0, buf.as_mut_ptr(), buf.len()) };
615        Some(unsafe { CStr::from_ptr(buf.as_ptr()).to_string_lossy().to_string() })
616    }
617
618    /// Checks if a command event represents a "checked" state.
619    pub fn is_checked(&self) -> Option<bool> {
620        if self.0.is_null() {
621            return None;
622        }
623        Some(unsafe { ffi::wxd_CommandEvent_IsChecked(self.0) })
624    }
625
626    /// Gets the mouse position associated with a mouse event.
627    pub fn get_position(&self) -> Option<Point> {
628        if self.0.is_null() {
629            return None;
630        }
631        let c_point = unsafe { ffi::wxd_MouseEvent_GetPosition(self.0) };
632        if c_point.x == -1 && c_point.y == -1 {
633            None
634        } else {
635            Some(Point {
636                x: c_point.x,
637                y: c_point.y,
638            })
639        }
640    }
641
642    /// Gets the wheel rotation value associated with a mouse wheel event.
643    /// Returns the wheel rotation amount in multiples of wheel delta.
644    /// Positive values indicate forward/up scrolling, negative values indicate backward/down scrolling.
645    pub fn get_wheel_rotation(&self) -> i32 {
646        if self.0.is_null() {
647            return 0;
648        }
649        unsafe { ffi::wxd_MouseEvent_GetWheelRotation(self.0) }
650    }
651
652    /// Gets the wheel delta value associated with a mouse wheel event.
653    /// This is the basic unit of wheel rotation, typically 120 on most systems.
654    /// The actual rotation can be calculated as get_wheel_rotation() / get_wheel_delta().
655    pub fn get_wheel_delta(&self) -> i32 {
656        if self.0.is_null() {
657            return 120; // Default wheel delta
658        }
659        unsafe { ffi::wxd_MouseEvent_GetWheelDelta(self.0) }
660    }
661
662    /// Gets the key code associated with a key event.
663    pub fn get_key_code(&self) -> Option<i32> {
664        if self.0.is_null() {
665            return None;
666        }
667        let key_code = unsafe { ffi::wxd_KeyEvent_GetKeyCode(self.0) };
668        if key_code == 0 { None } else { Some(key_code) }
669    }
670
671    /// Gets the key unicode value associated with a key event.
672    pub fn get_unicode_key(&self) -> Option<i32> {
673        if self.0.is_null() {
674            return None;
675        }
676        let key_code = unsafe { ffi::wxd_KeyEvent_GetUnicodeKey(self.0) };
677        if key_code == 0 { None } else { Some(key_code) }
678    }
679
680    /// Gets the integer value associated with a command event.
681    pub fn get_int(&self) -> Option<i32> {
682        if self.0.is_null() {
683            return None;
684        }
685        let int_val = unsafe { ffi::wxd_CommandEvent_GetInt(self.0) };
686        if int_val == -1 { None } else { Some(int_val) }
687    }
688
689    /// Check if the Control key is pressed during this key event
690    pub fn control_down(&self) -> bool {
691        if self.0.is_null() {
692            return false;
693        }
694        unsafe { ffi::wxd_KeyEvent_ControlDown(self.0) }
695    }
696
697    /// Check if the Shift key is pressed during this key event
698    pub fn shift_down(&self) -> bool {
699        if self.0.is_null() {
700            return false;
701        }
702        unsafe { ffi::wxd_KeyEvent_ShiftDown(self.0) }
703    }
704
705    /// Check if the Alt key is pressed during this key event
706    pub fn alt_down(&self) -> bool {
707        if self.0.is_null() {
708            return false;
709        }
710        unsafe { ffi::wxd_KeyEvent_AltDown(self.0) }
711    }
712
713    /// Check if the Meta key is pressed during this key event (Cmd on macOS, Windows key on Windows)
714    pub fn meta_down(&self) -> bool {
715        if self.0.is_null() {
716            return false;
717        }
718        unsafe { ffi::wxd_KeyEvent_MetaDown(self.0) }
719    }
720
721    /// Check if the platform-specific command key is pressed (Cmd on macOS, Ctrl on Windows/Linux)
722    pub fn cmd_down(&self) -> bool {
723        if self.0.is_null() {
724            return false;
725        }
726        unsafe { ffi::wxd_KeyEvent_CmdDown(self.0) }
727    }
728
729    /// Requests more idle events to be sent.
730    /// This should only be called from an idle event handler.
731    /// When `need_more` is true, the system will continue sending idle events.
732    /// When false, idle events will stop until triggered by other activity.
733    pub fn request_more(&self, need_more: bool) {
734        if self.0.is_null() {
735            return;
736        }
737        unsafe {
738            ffi::wxd_IdleEvent_RequestMore(self.0, need_more);
739        }
740    }
741
742    /// Returns true if more idle events have been requested.
743    /// This can be used to check if the idle event handler requested more processing.
744    pub fn more_requested(&self) -> bool {
745        if self.0.is_null() {
746            return false;
747        }
748        unsafe { ffi::wxd_IdleEvent_MoreRequested(self.0) }
749    }
750
751    /// Checks if an event can be vetoed.
752    /// This works with all vetable events (close events, tree events, list events, etc.)
753    /// to determine if the application can prevent the event's default action.
754    /// Note: This method now uses the general veto system and works with all vetable events.
755    pub fn can_veto(&self) -> bool {
756        if self.0.is_null() {
757            return false;
758        }
759        unsafe { ffi::wxd_Event_CanVeto(self.0) }
760    }
761
762    /// Vetos an event, preventing its default action.
763    /// This should only be called if `can_veto()` returns true.
764    /// Works with all vetable events (close events, tree events, list events, etc.).
765    /// When called on a close event, it prevents the window from being closed.
766    /// When called on other events, it prevents their respective default actions.
767    /// The event handler should provide feedback to the user about why the action was cancelled.
768    /// Note: This method now uses the general veto system and works with all vetable events.
769    pub fn veto(&self) {
770        if self.0.is_null() {
771            return;
772        }
773        unsafe { ffi::wxd_Event_Veto(self.0) }
774    }
775
776    /// General method to check if any event was vetoed.
777    /// Works with all vetable events (wxCloseEvent, wxNotifyEvent derivatives, etc.)
778    pub fn is_vetoed(&self) -> bool {
779        if self.0.is_null() {
780            return false;
781        }
782        unsafe { ffi::wxd_Event_IsVetoed(self.0) }
783    }
784
785    /// Sets whether an event can be vetoed.
786    /// This method only applies to events that support veto functionality.
787    /// For wxCloseEvent: controls whether the close event can be cancelled
788    /// For other vetable events: this method exists for API completeness but may not have effect
789    /// as most other vetable events (derived from wxNotifyEvent) are always vetable
790    pub fn set_can_veto(&self, can_veto: bool) {
791        if self.0.is_null() {
792            return;
793        }
794        unsafe { ffi::wxd_Event_SetCanVeto(self.0, can_veto) }
795    }
796}
797
798// --- WxEvtHandler Trait ---
799
800pub trait WxEvtHandler {
801    /// Returns the raw event handler pointer for this widget.
802    ///
803    /// # Safety
804    /// The caller must ensure the returned pointer is valid and not null.
805    /// The pointer must point to a valid wxEvtHandler object that remains valid
806    /// for the lifetime of this widget.
807    unsafe fn get_event_handler_ptr(&self) -> *mut ffi::wxd_EvtHandler_t;
808
809    // Internal implementation with crate visibility
810    #[doc(hidden)]
811    fn bind_internal<F>(&self, event_type: EventType, callback: F) -> EventToken
812    where
813        F: FnMut(Event) + 'static,
814    {
815        let handler_ptr = unsafe { self.get_event_handler_ptr() };
816        if handler_ptr.is_null() {
817            /* ... error handling ... */
818            return EventToken::INVALID_TOKEN;
819        }
820
821        // Double-box the callback to match trampoline expectations
822        let boxed_callback: Box<dyn FnMut(Event) + 'static> = Box::new(callback);
823        let double_boxed = Box::new(boxed_callback);
824        let user_data = Box::into_raw(double_boxed) as *mut c_void;
825
826        type TrampolineFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
827        let trampoline_ptr: TrampolineFn = rust_event_handler_trampoline;
828        let trampoline_c_void = trampoline_ptr as *mut c_void;
829
830        // use the callback closure pointer as the token identifier
831        let token = EventToken::from(user_data as usize);
832
833        let et = event_type.bits();
834        unsafe { ffi::wxd_EvtHandler_Bind(handler_ptr, et, trampoline_c_void, user_data, token.into()) };
835
836        token
837    }
838
839    /// Unbind a specific event handler by token.
840    ///
841    /// Returns `true` if the handler was found and removed, `false` otherwise.
842    ///
843    /// # Example
844    ///
845    /// ```ignore
846    /// let token = button.on_click(|_| println!("clicked"));
847    /// // ... later ...
848    /// button.unbind(token);  // Remove this specific handler
849    /// ```
850    fn unbind(&self, token: EventToken) -> bool {
851        let handler_ptr = unsafe { self.get_event_handler_ptr() };
852        if handler_ptr.is_null() || !token.is_valid() {
853            return false;
854        }
855
856        unsafe { ffi::wxd_EvtHandler_Unbind(handler_ptr, token.into()) }
857    }
858
859    /// Unbind all event handlers currently attached to this handler.
860    ///
861    /// Returns the number of handlers removed.
862    fn unbind_all(&self) -> usize {
863        let handler_ptr = unsafe { self.get_event_handler_ptr() };
864        if handler_ptr.is_null() {
865            return 0;
866        }
867        unsafe { ffi::wxd_EvtHandler_UnbindAll(handler_ptr) as usize }
868    }
869
870    // Internal implementation with ID support for tools and menu items
871    #[doc(hidden)]
872    fn bind_with_id_internal<F>(&self, event_type: EventType, id: i32, callback: F) -> EventToken
873    where
874        F: FnMut(Event) + 'static,
875    {
876        let handler_ptr = unsafe { self.get_event_handler_ptr() };
877        if handler_ptr.is_null() {
878            /* ... error handling ... */
879            return EventToken::INVALID_TOKEN;
880        }
881
882        // Double-box the callback to match trampoline expectations
883        let boxed_callback: Box<dyn FnMut(Event) + 'static> = Box::new(callback);
884        let double_boxed = Box::new(boxed_callback);
885        let user_data = Box::into_raw(double_boxed) as *mut c_void;
886
887        type TrampolineFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
888        let trampoline_ptr: TrampolineFn = rust_event_handler_trampoline;
889        let trampoline_c_void = trampoline_ptr as *mut c_void;
890
891        // use the callback closure pointer as the token identifier
892        let token = EventToken::from(user_data as usize);
893
894        let et = event_type.bits();
895        unsafe { ffi::wxd_EvtHandler_BindWithId(handler_ptr, et, id, trampoline_c_void, user_data, token.into()) };
896
897        token
898    }
899}
900
901// --- FFI Trampoline & Drop Functions (Updated for Simple Event) ---
902
903/// Trampoline function: Called by C++.
904/// `user_data` is a raw pointer to `Box<dyn FnMut(Event) + 'static>`.
905///
906/// # Safety
907/// This function is called from C++ code and must maintain the following invariants:
908/// - `user_data` must be a valid pointer to a `Box<Box<dyn FnMut(Event) + 'static>>`
909/// - `event_ptr_cvoid` must be a valid pointer to a `wxd_Event_t` object
910/// - The pointers must remain valid for the duration of this function call
911/// - This function must not be called from multiple threads simultaneously
912#[unsafe(no_mangle)]
913pub unsafe extern "C" fn rust_event_handler_trampoline(user_data: *mut c_void, event_ptr_cvoid: *mut c_void) {
914    if user_data.is_null() {
915        /* ... error handling ... */
916        return;
917    }
918
919    // Cast to Box<dyn FnMut(Event)> directly
920    let closure_box = unsafe { &mut *(user_data as *mut Box<dyn FnMut(Event) + 'static>) };
921    let event_ptr = event_ptr_cvoid as *mut ffi::wxd_Event_t;
922
923    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
924        // UPDATED: Create simple Event
925        let safe_event = unsafe { Event::from_ptr(event_ptr) };
926        (*closure_box)(safe_event);
927    }));
928
929    if result.is_err() { /* ... error handling ... */ }
930}
931
932/// Function called by C++ to drop the Rust closure Box.
933/// `ptr` is a raw pointer to `Box<dyn FnMut(Event) + 'static>`.
934///
935/// # Safety
936/// This function is called from C++ code to clean up Rust callbacks.
937/// - `ptr` must be a valid pointer to a `Box<Box<dyn FnMut(Event) + 'static>>`
938///   that was previously allocated by Rust
939/// - The pointer must not be used after this function returns
940/// - This function must only be called once per pointer
941#[unsafe(no_mangle)]
942pub unsafe extern "C" fn drop_rust_event_closure_box(ptr: *mut c_void) {
943    if !ptr.is_null() {
944        // Drop the Box<dyn FnMut(Event)>
945        log::trace!("Dropping Rust event closure box at ptr: {ptr:?}");
946        let _ = unsafe { Box::from_raw(ptr as *mut Box<dyn FnMut(Event) + 'static>) };
947    }
948}