ranma 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +5571 -0
- data/Cargo.toml +3 -0
- data/LICENSE +21 -0
- data/README.md +293 -0
- data/ext/ranma/Cargo.toml +23 -0
- data/ext/ranma/extconf.rb +4 -0
- data/ext/ranma/src/app.rs +231 -0
- data/ext/ranma/src/blit.wgsl +32 -0
- data/ext/ranma/src/clipboard.rs +57 -0
- data/ext/ranma/src/dpi.rs +227 -0
- data/ext/ranma/src/event_loop.rs +145 -0
- data/ext/ranma/src/events.rs +305 -0
- data/ext/ranma/src/hotkey.rs +245 -0
- data/ext/ranma/src/ime.rs +318 -0
- data/ext/ranma/src/lib.rs +42 -0
- data/ext/ranma/src/menu.rs +588 -0
- data/ext/ranma/src/monitor.rs +149 -0
- data/ext/ranma/src/painter.rs +1082 -0
- data/ext/ranma/src/proxy.rs +34 -0
- data/ext/ranma/src/surface.rs +389 -0
- data/ext/ranma/src/theme.rs +20 -0
- data/ext/ranma/src/tray.rs +251 -0
- data/ext/ranma/src/webview.rs +334 -0
- data/ext/ranma/src/window.rs +691 -0
- data/ext/ranma/src/window_store.rs +81 -0
- data/lib/ranma/version.rb +3 -0
- data/lib/ranma.rb +61 -0
- metadata +86 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
use std::cell::RefCell;
|
|
2
|
+
use magnus::{function, method, prelude::*, Error, Ruby, RArray, RHash, Value, TryConvert};
|
|
3
|
+
use tao::window::{Window, WindowBuilder, WindowId};
|
|
4
|
+
use tao::event_loop::EventLoopWindowTarget;
|
|
5
|
+
use tao::dpi::{LogicalSize, Size, Position, LogicalPosition};
|
|
6
|
+
use crate::dpi::{RbPhysicalSize, RbLogicalSize, RbPhysicalPosition, RbLogicalPosition};
|
|
7
|
+
use crate::monitor::{RbMonitorHandle, RbVideoMode, get_tao_video_mode};
|
|
8
|
+
use crate::app;
|
|
9
|
+
use crate::window_store;
|
|
10
|
+
|
|
11
|
+
#[magnus::wrap(class = "Ranma::Window", free_immediately, size)]
|
|
12
|
+
pub struct RbWindow {
|
|
13
|
+
inner: RefCell<Option<Window>>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl RbWindow {
|
|
17
|
+
pub fn new_from_tao(window: Window) -> Self {
|
|
18
|
+
Self {
|
|
19
|
+
inner: RefCell::new(Some(window)),
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fn with_window<F, R>(&self, f: F) -> Result<R, Error>
|
|
24
|
+
where
|
|
25
|
+
F: FnOnce(&Window) -> R,
|
|
26
|
+
{
|
|
27
|
+
let borrow = self.inner.borrow();
|
|
28
|
+
match borrow.as_ref() {
|
|
29
|
+
Some(w) => Ok(f(w)),
|
|
30
|
+
None => Err(Error::new(
|
|
31
|
+
unsafe { Ruby::get_unchecked() }.exception_runtime_error(),
|
|
32
|
+
"Window has been destroyed",
|
|
33
|
+
)),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn id(&self) -> Result<String, Error> {
|
|
38
|
+
self.with_window(|w| format!("{:?}", w.id()))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn title(&self) -> Result<String, Error> {
|
|
42
|
+
self.with_window(|w| w.title())
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pub fn set_title(&self, title: String) -> Result<(), Error> {
|
|
46
|
+
self.with_window(|w| w.set_title(&title))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pub fn inner_size(&self) -> Result<RbPhysicalSize, Error> {
|
|
50
|
+
self.with_window(|w| w.inner_size().into())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub fn outer_size(&self) -> Result<RbPhysicalSize, Error> {
|
|
54
|
+
self.with_window(|w| w.outer_size().into())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pub fn set_inner_size(&self, size: Value) -> Result<(), Error> {
|
|
58
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
59
|
+
let s = value_to_size(&ruby, size)?;
|
|
60
|
+
self.with_window(|w| w.set_inner_size(s))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pub fn outer_position(&self) -> Result<RbPhysicalPosition, Error> {
|
|
64
|
+
self.with_window(|w| {
|
|
65
|
+
w.outer_position()
|
|
66
|
+
.map(|p| p.into())
|
|
67
|
+
.unwrap_or(RbPhysicalPosition::new(0, 0))
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pub fn set_outer_position(&self, pos: Value) -> Result<(), Error> {
|
|
72
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
73
|
+
let p = value_to_position(&ruby, pos)?;
|
|
74
|
+
self.with_window(|w| w.set_outer_position(p))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
pub fn set_visible(&self, visible: bool) -> Result<(), Error> {
|
|
78
|
+
self.with_window(|w| w.set_visible(visible))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub fn is_visible(&self) -> Result<bool, Error> {
|
|
82
|
+
self.with_window(|w| w.is_visible())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
pub fn set_resizable(&self, resizable: bool) -> Result<(), Error> {
|
|
86
|
+
self.with_window(|w| w.set_resizable(resizable))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
pub fn is_resizable(&self) -> Result<bool, Error> {
|
|
90
|
+
self.with_window(|w| w.is_resizable())
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
pub fn set_decorations(&self, decorations: bool) -> Result<(), Error> {
|
|
94
|
+
self.with_window(|w| w.set_decorations(decorations))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pub fn is_decorated(&self) -> Result<bool, Error> {
|
|
98
|
+
self.with_window(|w| w.is_decorated())
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
pub fn set_always_on_top(&self, always_on_top: bool) -> Result<(), Error> {
|
|
102
|
+
self.with_window(|w| w.set_always_on_top(always_on_top))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
pub fn is_always_on_top(&self) -> Result<bool, Error> {
|
|
106
|
+
self.with_window(|w| w.is_always_on_top())
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pub fn set_maximized(&self, maximized: bool) -> Result<(), Error> {
|
|
110
|
+
self.with_window(|w| w.set_maximized(maximized))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
pub fn is_maximized(&self) -> Result<bool, Error> {
|
|
114
|
+
self.with_window(|w| w.is_maximized())
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
pub fn set_minimized(&self, minimized: bool) -> Result<(), Error> {
|
|
118
|
+
self.with_window(|w| w.set_minimized(minimized))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pub fn is_minimized(&self) -> Result<bool, Error> {
|
|
122
|
+
self.with_window(|w| w.is_minimized())
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pub fn set_fullscreen(&self, fullscreen: bool) -> Result<(), Error> {
|
|
126
|
+
self.with_window(|w| {
|
|
127
|
+
if fullscreen {
|
|
128
|
+
w.set_fullscreen(Some(tao::window::Fullscreen::Borderless(None)));
|
|
129
|
+
} else {
|
|
130
|
+
w.set_fullscreen(None);
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
pub fn is_fullscreen(&self) -> Result<bool, Error> {
|
|
136
|
+
self.with_window(|w| w.fullscreen().is_some())
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
pub fn set_cursor_visible(&self, visible: bool) -> Result<(), Error> {
|
|
140
|
+
self.with_window(|w| w.set_cursor_visible(visible))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
pub fn request_redraw(&self) -> Result<(), Error> {
|
|
144
|
+
self.with_window(|w| w.request_redraw())
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
pub fn scale_factor(&self) -> Result<f64, Error> {
|
|
148
|
+
self.with_window(|w| w.scale_factor())
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
pub fn set_focus(&self) -> Result<(), Error> {
|
|
152
|
+
self.with_window(|w| w.set_focus())
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
pub fn is_focused(&self) -> Result<bool, Error> {
|
|
156
|
+
self.with_window(|w| w.is_focused())
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
pub fn drag_window(&self) -> Result<(), Error> {
|
|
160
|
+
self.with_window(|w| {
|
|
161
|
+
let _ = w.drag_window();
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
pub fn destroy(&self) -> Result<(), Error> {
|
|
166
|
+
let mut borrow = self.inner.borrow_mut();
|
|
167
|
+
*borrow = None;
|
|
168
|
+
Ok(())
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
pub fn inspect(&self) -> String {
|
|
172
|
+
let borrow = self.inner.borrow();
|
|
173
|
+
match borrow.as_ref() {
|
|
174
|
+
Some(w) => format!("#<Ranma::Window id={:?} title={:?}>", w.id(), w.title()),
|
|
175
|
+
None => "#<Ranma::Window (destroyed)>".to_string(),
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
pub fn value_to_size(_ruby: &Ruby, value: Value) -> Result<Size, Error> {
|
|
181
|
+
if let Ok(ls) = <&RbLogicalSize>::try_convert(value) {
|
|
182
|
+
return Ok(Size::Logical((*ls).into()));
|
|
183
|
+
}
|
|
184
|
+
if let Ok(ps) = <&RbPhysicalSize>::try_convert(value) {
|
|
185
|
+
return Ok(Size::Physical((*ps).into()));
|
|
186
|
+
}
|
|
187
|
+
if let Ok(arr) = <(f64, f64)>::try_convert(value) {
|
|
188
|
+
return Ok(Size::Logical(LogicalSize::new(arr.0, arr.1)));
|
|
189
|
+
}
|
|
190
|
+
Err(Error::new(
|
|
191
|
+
unsafe { Ruby::get_unchecked() }.exception_type_error(),
|
|
192
|
+
"Expected PhysicalSize, LogicalSize, or [width, height]",
|
|
193
|
+
))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
pub fn value_to_position(_ruby: &Ruby, value: Value) -> Result<Position, Error> {
|
|
197
|
+
if let Ok(lp) = <&RbLogicalPosition>::try_convert(value) {
|
|
198
|
+
return Ok(Position::Logical((*lp).into()));
|
|
199
|
+
}
|
|
200
|
+
if let Ok(pp) = <&RbPhysicalPosition>::try_convert(value) {
|
|
201
|
+
return Ok(Position::Physical((*pp).into()));
|
|
202
|
+
}
|
|
203
|
+
if let Ok(arr) = <(f64, f64)>::try_convert(value) {
|
|
204
|
+
return Ok(Position::Logical(LogicalPosition::new(arr.0, arr.1)));
|
|
205
|
+
}
|
|
206
|
+
Err(Error::new(
|
|
207
|
+
unsafe { Ruby::get_unchecked() }.exception_type_error(),
|
|
208
|
+
"Expected PhysicalPosition, LogicalPosition, or [x, y]",
|
|
209
|
+
))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
pub fn create_window_from_hash(
|
|
213
|
+
ruby: &Ruby,
|
|
214
|
+
target: &EventLoopWindowTarget<()>,
|
|
215
|
+
opts: Option<RHash>,
|
|
216
|
+
) -> Result<RbWindow, Error> {
|
|
217
|
+
let mut builder = WindowBuilder::new();
|
|
218
|
+
|
|
219
|
+
if let Some(opts) = opts {
|
|
220
|
+
if let Some(title) = opts.get(ruby.sym_new("title")) {
|
|
221
|
+
if let Ok(t) = String::try_convert(title) {
|
|
222
|
+
builder = builder.with_title(t);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if let Some(size) = opts.get(ruby.sym_new("inner_size")) {
|
|
226
|
+
let s = value_to_size(ruby, size)?;
|
|
227
|
+
builder = builder.with_inner_size(s);
|
|
228
|
+
}
|
|
229
|
+
if let Some(min_size) = opts.get(ruby.sym_new("min_inner_size")) {
|
|
230
|
+
let s = value_to_size(ruby, min_size)?;
|
|
231
|
+
builder = builder.with_min_inner_size(s);
|
|
232
|
+
}
|
|
233
|
+
if let Some(max_size) = opts.get(ruby.sym_new("max_inner_size")) {
|
|
234
|
+
let s = value_to_size(ruby, max_size)?;
|
|
235
|
+
builder = builder.with_max_inner_size(s);
|
|
236
|
+
}
|
|
237
|
+
if let Some(pos) = opts.get(ruby.sym_new("position")) {
|
|
238
|
+
let p = value_to_position(ruby, pos)?;
|
|
239
|
+
builder = builder.with_position(p);
|
|
240
|
+
}
|
|
241
|
+
if let Some(resizable) = opts.get(ruby.sym_new("resizable")) {
|
|
242
|
+
if let Ok(r) = bool::try_convert(resizable) {
|
|
243
|
+
builder = builder.with_resizable(r);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if let Some(visible) = opts.get(ruby.sym_new("visible")) {
|
|
247
|
+
if let Ok(v) = bool::try_convert(visible) {
|
|
248
|
+
builder = builder.with_visible(v);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if let Some(decorations) = opts.get(ruby.sym_new("decorations")) {
|
|
252
|
+
if let Ok(d) = bool::try_convert(decorations) {
|
|
253
|
+
builder = builder.with_decorations(d);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if let Some(transparent) = opts.get(ruby.sym_new("transparent")) {
|
|
257
|
+
if let Ok(t) = bool::try_convert(transparent) {
|
|
258
|
+
builder = builder.with_transparent(t);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if let Some(maximized) = opts.get(ruby.sym_new("maximized")) {
|
|
262
|
+
if let Ok(m) = bool::try_convert(maximized) {
|
|
263
|
+
builder = builder.with_maximized(m);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if let Some(always_on_top) = opts.get(ruby.sym_new("always_on_top")) {
|
|
267
|
+
if let Ok(a) = bool::try_convert(always_on_top) {
|
|
268
|
+
builder = builder.with_always_on_top(a);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if let Some(focused) = opts.get(ruby.sym_new("focused")) {
|
|
272
|
+
if let Ok(f) = bool::try_convert(focused) {
|
|
273
|
+
builder = builder.with_focused(f);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let window = builder.build(target).map_err(|e| {
|
|
279
|
+
Error::new(
|
|
280
|
+
ruby.exception_runtime_error(),
|
|
281
|
+
format!("Failed to create window: {}", e),
|
|
282
|
+
)
|
|
283
|
+
})?;
|
|
284
|
+
|
|
285
|
+
Ok(RbWindow::new_from_tao(window))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
pub fn define_window_class(ruby: &Ruby, module: &magnus::RModule) -> Result<(), Error> {
|
|
289
|
+
let class = module.define_class("Window", ruby.class_object())?;
|
|
290
|
+
class.define_method("id", method!(RbWindow::id, 0))?;
|
|
291
|
+
class.define_method("title", method!(RbWindow::title, 0))?;
|
|
292
|
+
class.define_method("title=", method!(RbWindow::set_title, 1))?;
|
|
293
|
+
class.define_method("inner_size", method!(RbWindow::inner_size, 0))?;
|
|
294
|
+
class.define_method("outer_size", method!(RbWindow::outer_size, 0))?;
|
|
295
|
+
class.define_method("set_inner_size", method!(RbWindow::set_inner_size, 1))?;
|
|
296
|
+
class.define_method("outer_position", method!(RbWindow::outer_position, 0))?;
|
|
297
|
+
class.define_method("set_outer_position", method!(RbWindow::set_outer_position, 1))?;
|
|
298
|
+
class.define_method("visible=", method!(RbWindow::set_visible, 1))?;
|
|
299
|
+
class.define_method("visible?", method!(RbWindow::is_visible, 0))?;
|
|
300
|
+
class.define_method("resizable=", method!(RbWindow::set_resizable, 1))?;
|
|
301
|
+
class.define_method("resizable?", method!(RbWindow::is_resizable, 0))?;
|
|
302
|
+
class.define_method("decorations=", method!(RbWindow::set_decorations, 1))?;
|
|
303
|
+
class.define_method("decorated?", method!(RbWindow::is_decorated, 0))?;
|
|
304
|
+
class.define_method("always_on_top=", method!(RbWindow::set_always_on_top, 1))?;
|
|
305
|
+
class.define_method("always_on_top?", method!(RbWindow::is_always_on_top, 0))?;
|
|
306
|
+
class.define_method("maximized=", method!(RbWindow::set_maximized, 1))?;
|
|
307
|
+
class.define_method("maximized?", method!(RbWindow::is_maximized, 0))?;
|
|
308
|
+
class.define_method("minimized=", method!(RbWindow::set_minimized, 1))?;
|
|
309
|
+
class.define_method("minimized?", method!(RbWindow::is_minimized, 0))?;
|
|
310
|
+
class.define_method("fullscreen=", method!(RbWindow::set_fullscreen, 1))?;
|
|
311
|
+
class.define_method("fullscreen?", method!(RbWindow::is_fullscreen, 0))?;
|
|
312
|
+
class.define_method("cursor_visible=", method!(RbWindow::set_cursor_visible, 1))?;
|
|
313
|
+
class.define_method("request_redraw", method!(RbWindow::request_redraw, 0))?;
|
|
314
|
+
class.define_method("scale_factor", method!(RbWindow::scale_factor, 0))?;
|
|
315
|
+
class.define_method("focus", method!(RbWindow::set_focus, 0))?;
|
|
316
|
+
class.define_method("focused?", method!(RbWindow::is_focused, 0))?;
|
|
317
|
+
class.define_method("drag", method!(RbWindow::drag_window, 0))?;
|
|
318
|
+
class.define_method("destroy", method!(RbWindow::destroy, 0))?;
|
|
319
|
+
class.define_method("inspect", method!(RbWindow::inspect, 0))?;
|
|
320
|
+
class.define_method("to_s", method!(RbWindow::inspect, 0))?;
|
|
321
|
+
Ok(())
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// --- High-level API: RbAppWindow ---
|
|
325
|
+
|
|
326
|
+
#[magnus::wrap(class = "Ranma::AppWindow", free_immediately, size)]
|
|
327
|
+
pub struct RbAppWindow {
|
|
328
|
+
window_id: WindowId,
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
unsafe impl Send for RbAppWindow {}
|
|
332
|
+
|
|
333
|
+
impl RbAppWindow {
|
|
334
|
+
fn with_window<F, R>(&self, f: F) -> Result<R, Error>
|
|
335
|
+
where
|
|
336
|
+
F: FnOnce(&Window) -> R,
|
|
337
|
+
{
|
|
338
|
+
window_store::with_window(&self.window_id, f).ok_or_else(|| {
|
|
339
|
+
Error::new(
|
|
340
|
+
unsafe { Ruby::get_unchecked() }.exception_runtime_error(),
|
|
341
|
+
"Window has been destroyed",
|
|
342
|
+
)
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
pub fn new(opts: Option<RHash>) -> Result<Self, Error> {
|
|
347
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
348
|
+
|
|
349
|
+
let tao_window = app::with_event_loop_target(|target| {
|
|
350
|
+
let mut builder = WindowBuilder::new().with_visible(false);
|
|
351
|
+
|
|
352
|
+
if let Some(opts) = opts {
|
|
353
|
+
if let Some(title) = opts.get(ruby.sym_new("title")) {
|
|
354
|
+
if let Ok(t) = String::try_convert(title) {
|
|
355
|
+
builder = builder.with_title(t);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if let Some(size) = opts.get(ruby.sym_new("inner_size")) {
|
|
359
|
+
if let Ok(s) = value_to_size(&ruby, size) {
|
|
360
|
+
builder = builder.with_inner_size(s);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if let Some(min_size) = opts.get(ruby.sym_new("min_inner_size")) {
|
|
364
|
+
if let Ok(s) = value_to_size(&ruby, min_size) {
|
|
365
|
+
builder = builder.with_min_inner_size(s);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if let Some(max_size) = opts.get(ruby.sym_new("max_inner_size")) {
|
|
369
|
+
if let Ok(s) = value_to_size(&ruby, max_size) {
|
|
370
|
+
builder = builder.with_max_inner_size(s);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if let Some(pos) = opts.get(ruby.sym_new("position")) {
|
|
374
|
+
if let Ok(p) = value_to_position(&ruby, pos) {
|
|
375
|
+
builder = builder.with_position(p);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if let Some(resizable) = opts.get(ruby.sym_new("resizable")) {
|
|
379
|
+
if let Ok(r) = bool::try_convert(resizable) {
|
|
380
|
+
builder = builder.with_resizable(r);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if let Some(visible) = opts.get(ruby.sym_new("visible")) {
|
|
384
|
+
if let Ok(v) = bool::try_convert(visible) {
|
|
385
|
+
builder = builder.with_visible(v);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if let Some(decorations) = opts.get(ruby.sym_new("decorations")) {
|
|
389
|
+
if let Ok(d) = bool::try_convert(decorations) {
|
|
390
|
+
builder = builder.with_decorations(d);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if let Some(transparent) = opts.get(ruby.sym_new("transparent")) {
|
|
394
|
+
if let Ok(t) = bool::try_convert(transparent) {
|
|
395
|
+
builder = builder.with_transparent(t);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if let Some(maximized) = opts.get(ruby.sym_new("maximized")) {
|
|
399
|
+
if let Ok(m) = bool::try_convert(maximized) {
|
|
400
|
+
builder = builder.with_maximized(m);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if let Some(always_on_top) = opts.get(ruby.sym_new("always_on_top")) {
|
|
404
|
+
if let Ok(a) = bool::try_convert(always_on_top) {
|
|
405
|
+
builder = builder.with_always_on_top(a);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if let Some(focused) = opts.get(ruby.sym_new("focused")) {
|
|
409
|
+
if let Ok(f) = bool::try_convert(focused) {
|
|
410
|
+
builder = builder.with_focused(f);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
builder.build(target)
|
|
416
|
+
})
|
|
417
|
+
.ok_or_else(|| {
|
|
418
|
+
Error::new(
|
|
419
|
+
ruby.exception_runtime_error(),
|
|
420
|
+
"Cannot create window: event loop is not running. Call App.start first.",
|
|
421
|
+
)
|
|
422
|
+
})?
|
|
423
|
+
.map_err(|e| {
|
|
424
|
+
Error::new(
|
|
425
|
+
ruby.exception_runtime_error(),
|
|
426
|
+
format!("Failed to create window: {}", e),
|
|
427
|
+
)
|
|
428
|
+
})?;
|
|
429
|
+
|
|
430
|
+
let window_id = tao_window.id();
|
|
431
|
+
window_store::register(window_id, tao_window);
|
|
432
|
+
|
|
433
|
+
Ok(RbAppWindow { window_id })
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
pub fn raw_window_id(&self) -> WindowId {
|
|
437
|
+
self.window_id
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
pub fn on_event(&self, handler: Value) -> Result<(), Error> {
|
|
441
|
+
window_store::set_event_handler(&self.window_id, handler);
|
|
442
|
+
Ok(())
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
pub fn id(&self) -> String {
|
|
446
|
+
format!("{:?}", self.window_id)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
pub fn title(&self) -> Result<String, Error> {
|
|
450
|
+
self.with_window(|w| w.title())
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
pub fn set_title(&self, title: String) -> Result<(), Error> {
|
|
454
|
+
self.with_window(|w| w.set_title(&title))
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
pub fn inner_size(&self) -> Result<RbPhysicalSize, Error> {
|
|
458
|
+
self.with_window(|w| w.inner_size().into())
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
pub fn outer_size(&self) -> Result<RbPhysicalSize, Error> {
|
|
462
|
+
self.with_window(|w| w.outer_size().into())
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
pub fn set_inner_size(&self, size: Value) -> Result<(), Error> {
|
|
466
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
467
|
+
let s = value_to_size(&ruby, size)?;
|
|
468
|
+
self.with_window(|w| w.set_inner_size(s))
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
pub fn outer_position(&self) -> Result<RbPhysicalPosition, Error> {
|
|
472
|
+
self.with_window(|w| {
|
|
473
|
+
w.outer_position()
|
|
474
|
+
.map(|p| p.into())
|
|
475
|
+
.unwrap_or(RbPhysicalPosition::new(0, 0))
|
|
476
|
+
})
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
pub fn set_outer_position(&self, pos: Value) -> Result<(), Error> {
|
|
480
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
481
|
+
let p = value_to_position(&ruby, pos)?;
|
|
482
|
+
self.with_window(|w| w.set_outer_position(p))
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
pub fn set_visible(&self, visible: bool) -> Result<(), Error> {
|
|
486
|
+
self.with_window(|w| w.set_visible(visible))
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
pub fn is_visible(&self) -> Result<bool, Error> {
|
|
490
|
+
self.with_window(|w| w.is_visible())
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
pub fn set_resizable(&self, resizable: bool) -> Result<(), Error> {
|
|
494
|
+
self.with_window(|w| w.set_resizable(resizable))
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
pub fn is_resizable(&self) -> Result<bool, Error> {
|
|
498
|
+
self.with_window(|w| w.is_resizable())
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
pub fn set_decorations(&self, decorations: bool) -> Result<(), Error> {
|
|
502
|
+
self.with_window(|w| w.set_decorations(decorations))
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
pub fn is_decorated(&self) -> Result<bool, Error> {
|
|
506
|
+
self.with_window(|w| w.is_decorated())
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
pub fn set_always_on_top(&self, always_on_top: bool) -> Result<(), Error> {
|
|
510
|
+
self.with_window(|w| w.set_always_on_top(always_on_top))
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
pub fn is_always_on_top(&self) -> Result<bool, Error> {
|
|
514
|
+
self.with_window(|w| w.is_always_on_top())
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
pub fn set_maximized(&self, maximized: bool) -> Result<(), Error> {
|
|
518
|
+
self.with_window(|w| w.set_maximized(maximized))
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
pub fn is_maximized(&self) -> Result<bool, Error> {
|
|
522
|
+
self.with_window(|w| w.is_maximized())
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
pub fn set_minimized(&self, minimized: bool) -> Result<(), Error> {
|
|
526
|
+
self.with_window(|w| w.set_minimized(minimized))
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
pub fn is_minimized(&self) -> Result<bool, Error> {
|
|
530
|
+
self.with_window(|w| w.is_minimized())
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
pub fn set_fullscreen(&self, fullscreen: bool) -> Result<(), Error> {
|
|
534
|
+
self.with_window(|w| {
|
|
535
|
+
if fullscreen {
|
|
536
|
+
w.set_fullscreen(Some(tao::window::Fullscreen::Borderless(None)));
|
|
537
|
+
} else {
|
|
538
|
+
w.set_fullscreen(None);
|
|
539
|
+
}
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
pub fn is_fullscreen(&self) -> Result<bool, Error> {
|
|
544
|
+
self.with_window(|w| w.fullscreen().is_some())
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
pub fn set_cursor_visible(&self, visible: bool) -> Result<(), Error> {
|
|
548
|
+
self.with_window(|w| w.set_cursor_visible(visible))
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
pub fn request_redraw(&self) -> Result<(), Error> {
|
|
552
|
+
self.with_window(|w| w.request_redraw())
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
pub fn scale_factor(&self) -> Result<f64, Error> {
|
|
556
|
+
self.with_window(|w| w.scale_factor())
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
pub fn set_focus(&self) -> Result<(), Error> {
|
|
560
|
+
self.with_window(|w| w.set_focus())
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
pub fn is_focused(&self) -> Result<bool, Error> {
|
|
564
|
+
self.with_window(|w| w.is_focused())
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
pub fn drag_window(&self) -> Result<(), Error> {
|
|
568
|
+
self.with_window(|w| {
|
|
569
|
+
let _ = w.drag_window();
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
pub fn set_ime_position(&self, pos: Value) -> Result<(), Error> {
|
|
574
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
575
|
+
let p = value_to_position(&ruby, pos)?;
|
|
576
|
+
self.with_window(|w| w.set_ime_position(p))
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
pub fn current_monitor(&self) -> Result<Option<RbMonitorHandle>, Error> {
|
|
580
|
+
self.with_window(|w| w.current_monitor().map(RbMonitorHandle::new))
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
pub fn available_monitors(&self) -> Result<RArray, Error> {
|
|
584
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
585
|
+
self.with_window(|w| {
|
|
586
|
+
let arr = ruby.ary_new();
|
|
587
|
+
for mon in w.available_monitors() {
|
|
588
|
+
let _ = arr.push(RbMonitorHandle::new(mon));
|
|
589
|
+
}
|
|
590
|
+
arr
|
|
591
|
+
})
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
pub fn primary_monitor(&self) -> Result<Option<RbMonitorHandle>, Error> {
|
|
595
|
+
self.with_window(|w| w.primary_monitor().map(RbMonitorHandle::new))
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
pub fn set_fullscreen_exclusive(&self, video_mode: &RbVideoMode) -> Result<(), Error> {
|
|
599
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
600
|
+
let tao_vm = get_tao_video_mode(video_mode.index()).ok_or_else(|| {
|
|
601
|
+
Error::new(ruby.exception_runtime_error(), "VideoMode not found")
|
|
602
|
+
})?;
|
|
603
|
+
self.with_window(|w| {
|
|
604
|
+
w.set_fullscreen(Some(tao::window::Fullscreen::Exclusive(tao_vm)));
|
|
605
|
+
})
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
pub fn set_fullscreen_borderless(&self, monitor: Option<&RbMonitorHandle>) -> Result<(), Error> {
|
|
609
|
+
let mon = monitor.map(|m| m.inner_clone());
|
|
610
|
+
self.with_window(|w| {
|
|
611
|
+
w.set_fullscreen(Some(tao::window::Fullscreen::Borderless(mon)));
|
|
612
|
+
})
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
#[cfg(target_os = "macos")]
|
|
616
|
+
pub fn ns_view(&self) -> Result<usize, Error> {
|
|
617
|
+
use tao::platform::macos::WindowExtMacOS;
|
|
618
|
+
self.with_window(|w| w.ns_view() as usize)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
#[cfg(not(target_os = "macos"))]
|
|
622
|
+
pub fn ns_view(&self) -> Result<usize, Error> {
|
|
623
|
+
Ok(0)
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
pub fn setup_ime_preedit(&self) -> Result<(), Error> {
|
|
627
|
+
let ns_view = self.ns_view()?;
|
|
628
|
+
crate::ime::setup_ime_swizzle(self.window_id, ns_view);
|
|
629
|
+
Ok(())
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
pub fn destroy(&self) -> Result<(), Error> {
|
|
633
|
+
window_store::remove(&self.window_id);
|
|
634
|
+
Ok(())
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
pub fn inspect(&self) -> Result<String, Error> {
|
|
638
|
+
match window_store::with_window(&self.window_id, |w| {
|
|
639
|
+
format!("#<Ranma::AppWindow id={:?} title={:?}>", w.id(), w.title())
|
|
640
|
+
}) {
|
|
641
|
+
Some(s) => Ok(s),
|
|
642
|
+
None => Ok("#<Ranma::AppWindow (destroyed)>".to_string()),
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
pub fn define_app_window_class(ruby: &Ruby, module: &magnus::RModule) -> Result<(), Error> {
|
|
648
|
+
let class = module.define_class("AppWindow", ruby.class_object())?;
|
|
649
|
+
class.define_singleton_method("new", function!(RbAppWindow::new, 1))?;
|
|
650
|
+
class.define_method("on_event", method!(RbAppWindow::on_event, 1))?;
|
|
651
|
+
class.define_method("id", method!(RbAppWindow::id, 0))?;
|
|
652
|
+
class.define_method("title", method!(RbAppWindow::title, 0))?;
|
|
653
|
+
class.define_method("title=", method!(RbAppWindow::set_title, 1))?;
|
|
654
|
+
class.define_method("inner_size", method!(RbAppWindow::inner_size, 0))?;
|
|
655
|
+
class.define_method("outer_size", method!(RbAppWindow::outer_size, 0))?;
|
|
656
|
+
class.define_method("set_inner_size", method!(RbAppWindow::set_inner_size, 1))?;
|
|
657
|
+
class.define_method("outer_position", method!(RbAppWindow::outer_position, 0))?;
|
|
658
|
+
class.define_method("set_outer_position", method!(RbAppWindow::set_outer_position, 1))?;
|
|
659
|
+
class.define_method("visible=", method!(RbAppWindow::set_visible, 1))?;
|
|
660
|
+
class.define_method("visible?", method!(RbAppWindow::is_visible, 0))?;
|
|
661
|
+
class.define_method("resizable=", method!(RbAppWindow::set_resizable, 1))?;
|
|
662
|
+
class.define_method("resizable?", method!(RbAppWindow::is_resizable, 0))?;
|
|
663
|
+
class.define_method("decorations=", method!(RbAppWindow::set_decorations, 1))?;
|
|
664
|
+
class.define_method("decorated?", method!(RbAppWindow::is_decorated, 0))?;
|
|
665
|
+
class.define_method("always_on_top=", method!(RbAppWindow::set_always_on_top, 1))?;
|
|
666
|
+
class.define_method("always_on_top?", method!(RbAppWindow::is_always_on_top, 0))?;
|
|
667
|
+
class.define_method("maximized=", method!(RbAppWindow::set_maximized, 1))?;
|
|
668
|
+
class.define_method("maximized?", method!(RbAppWindow::is_maximized, 0))?;
|
|
669
|
+
class.define_method("minimized=", method!(RbAppWindow::set_minimized, 1))?;
|
|
670
|
+
class.define_method("minimized?", method!(RbAppWindow::is_minimized, 0))?;
|
|
671
|
+
class.define_method("fullscreen=", method!(RbAppWindow::set_fullscreen, 1))?;
|
|
672
|
+
class.define_method("fullscreen?", method!(RbAppWindow::is_fullscreen, 0))?;
|
|
673
|
+
class.define_method("cursor_visible=", method!(RbAppWindow::set_cursor_visible, 1))?;
|
|
674
|
+
class.define_method("request_redraw", method!(RbAppWindow::request_redraw, 0))?;
|
|
675
|
+
class.define_method("scale_factor", method!(RbAppWindow::scale_factor, 0))?;
|
|
676
|
+
class.define_method("focus", method!(RbAppWindow::set_focus, 0))?;
|
|
677
|
+
class.define_method("focused?", method!(RbAppWindow::is_focused, 0))?;
|
|
678
|
+
class.define_method("drag", method!(RbAppWindow::drag_window, 0))?;
|
|
679
|
+
class.define_method("set_ime_position", method!(RbAppWindow::set_ime_position, 1))?;
|
|
680
|
+
class.define_method("current_monitor", method!(RbAppWindow::current_monitor, 0))?;
|
|
681
|
+
class.define_method("available_monitors", method!(RbAppWindow::available_monitors, 0))?;
|
|
682
|
+
class.define_method("primary_monitor", method!(RbAppWindow::primary_monitor, 0))?;
|
|
683
|
+
class.define_method("set_fullscreen_exclusive", method!(RbAppWindow::set_fullscreen_exclusive, 1))?;
|
|
684
|
+
class.define_method("set_fullscreen_borderless", method!(RbAppWindow::set_fullscreen_borderless, 1))?;
|
|
685
|
+
class.define_method("ns_view", method!(RbAppWindow::ns_view, 0))?;
|
|
686
|
+
class.define_method("setup_ime_preedit", method!(RbAppWindow::setup_ime_preedit, 0))?;
|
|
687
|
+
class.define_method("destroy", method!(RbAppWindow::destroy, 0))?;
|
|
688
|
+
class.define_method("inspect", method!(RbAppWindow::inspect, 0))?;
|
|
689
|
+
class.define_method("to_s", method!(RbAppWindow::inspect, 0))?;
|
|
690
|
+
Ok(())
|
|
691
|
+
}
|