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
// SPDX-License-Identifier: MPL-2.0
//! A type-erased control.
use std::{cell::Cell, os::raw::c_void};
#[cfg(all(target_os = "windows", feature = "raw-window-handle"))]
use raw_window_handle::{
HasRawDisplayHandle,
HasRawWindowHandle,
RawDisplayHandle,
RawWindowHandle,
};
use crate::prelude::*;
impl Control {
/// Creates a new `Control` from a pointer.
///
/// By default, the new control is not a child of any parent control and will be destroyed on
/// drop.
///
/// # Safety
///
/// `ptr` must point to a valid `uiControl`.
#[inline]
pub(crate) unsafe fn from_ptr<'ui>(ptr: *mut uiControl) -> Self {
Self { ptr, is_child: Cell::new(false) }
}
}
/// A type-erased control.
///
/// This type provides functionality common to all controls and is accessible via
/// [`DerefMut::deref_mut`] on a valid control. When this type is dropped, the control is destroyed.
#[derive(Debug, Widget)]
#[widget(handle = "uiControl")]
pub struct Control {
ptr: *mut uiControl,
is_child: Cell<bool>,
}
impl Drop for Control {
fn drop(&mut self) {
// *libui-ng* automatically frees children controls, but's it our responsibility to free
// everything else.
if !self.is_child.get() {
let ptr = self.as_ptr();
tracing::debug!("Destroying control @ {:#?}", ptr);
unsafe { uiControlDestroy(ptr) };
}
}
}
impl Control {
/// Determines if this control is visible.
///
/// Controls are visible by default *except* for [`Window`](crate::Window)s, which are invisible
/// by default.
#[inline]
pub fn is_visible(&self) -> bool {
bool_from_libui(unsafe { uiControlVisible(self.as_ptr()) })
}
/// Determines if this control responds to user interaction.
///
/// Controls are enabled by default.
#[inline]
pub fn is_enabled(&self) -> bool {
bool_from_libui(unsafe { uiControlEnabled(self.as_ptr()) })
}
/// Determines if this control, and all of its parent controls, are enabled.
///
/// If this control is not a child of a parent control, then this function is equivalent to
/// [`is_enabled`](Self::is_enabled).
#[inline]
pub fn is_enabled_to_user(&self) -> bool {
bool_from_libui(unsafe { uiControlEnabledToUser(self.as_ptr()) })
}
/// A handle to the underlying OS object.
#[inline]
pub fn native_handle(&self) -> *mut c_void {
unsafe { uiControlHandle(self.as_ptr()) as *mut _ }
}
/// Makes this control [visible](Self::is_visible).
#[inline]
pub fn show(&self) {
unsafe { uiControlShow(self.as_ptr()) };
}
/// Makes this control not [visible](Self::is_visible).
#[inline]
pub fn hide(&self) {
unsafe { uiControlHide(self.as_ptr()) };
}
/// Makes this control [enabled](Self::is_enabled).
#[inline]
pub fn enable(&self) {
unsafe { uiControlEnable(self.as_ptr()) }
}
/// Makes this control not [enabled](Self::is_enabled).
#[inline]
pub fn disable(&self) {
unsafe { uiControlDisable(self.as_ptr()) }
}
/// Makes this control a child of another control.
///
/// This method **must** be called on child controls or else a double-free will occur. This is
/// because *libui-ng* automatically manages the memory of child controls, freeing them when
/// their parents are destroyed.
pub(crate) fn make_child(&self) {
// It's not strictly unsound to call this function twice, but it's probably indicative of a
// bug.
assert!(!self.is_child.get(), "double make_child()");
// *libui-ng* says windows can't be children.
assert_ne!(
unsafe { (*self.ptr).TypeSignature },
uiWindowSignature,
"uiWindows cannot be children",
);
self.is_child.set(true);
}
}
// On Windows (IDK about other OSes), all controls are windows. Hence, we should provide raw window
// handles for controls. This makes *boing* controls automagically compatible with dependents of
// *raw-window-handle* such as *wgpu*.
#[cfg(all(target_os = "windows", feature = "raw-window-handle"))]
unsafe impl HasRawWindowHandle for Control {
fn raw_window_handle(&self) -> RawWindowHandle {
use windows::Win32::UI::WindowsAndMessaging as wm;
let hwnd = self.native_handle();
let mut handle = raw_window_handle::Win32WindowHandle::empty();
handle.hinstance = unsafe {
wm::GetWindowLongW(
windows::Win32::Foundation::HWND(hwnd as isize),
wm::GWL_HINSTANCE,
)
} as *mut _;
handle.hwnd = hwnd;
RawWindowHandle::Win32(handle)
}
}
#[cfg(all(target_os = "windows", feature = "raw-window-handle"))]
unsafe impl HasRawDisplayHandle for Control {
#[inline]
fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Windows(raw_window_handle::WindowsDisplayHandle::empty())
}
}