657 lines
22 KiB
Rust
657 lines
22 KiB
Rust
|
|
use std::collections::VecDeque;
|
||
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||
|
|
use std::sync::{Arc, Mutex};
|
||
|
|
|
||
|
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||
|
|
use sctk::reexports::client::Display;
|
||
|
|
|
||
|
|
use sctk::reexports::calloop;
|
||
|
|
|
||
|
|
use sctk::window::{
|
||
|
|
ARGBColor, ButtonColorSpec, ColorSpec, ConceptConfig, ConceptFrame, Decorations,
|
||
|
|
};
|
||
|
|
|
||
|
|
use raw_window_handle::unix::WaylandHandle;
|
||
|
|
|
||
|
|
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||
|
|
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||
|
|
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||
|
|
use crate::platform::unix::{ARGBColor as LocalARGBColor, Button, ButtonState, Element, Theme};
|
||
|
|
use crate::platform_impl::{
|
||
|
|
MonitorHandle as PlatformMonitorHandle, OsError,
|
||
|
|
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||
|
|
};
|
||
|
|
use crate::window::{CursorIcon, Fullscreen, WindowAttributes};
|
||
|
|
|
||
|
|
use super::env::WindowingFeatures;
|
||
|
|
use super::event_loop::WinitState;
|
||
|
|
use super::output::{MonitorHandle, OutputManagerHandle};
|
||
|
|
use super::{EventLoopWindowTarget, WindowId};
|
||
|
|
|
||
|
|
pub mod shim;
|
||
|
|
|
||
|
|
use shim::{WindowHandle, WindowRequest, WindowUpdate};
|
||
|
|
|
||
|
|
pub struct Window {
|
||
|
|
/// Window id.
|
||
|
|
window_id: WindowId,
|
||
|
|
|
||
|
|
/// The Wayland display.
|
||
|
|
display: Display,
|
||
|
|
|
||
|
|
/// The underlying wl_surface.
|
||
|
|
surface: WlSurface,
|
||
|
|
|
||
|
|
/// The current window size.
|
||
|
|
size: Arc<Mutex<LogicalSize<u32>>>,
|
||
|
|
|
||
|
|
/// A handle to output manager.
|
||
|
|
output_manager_handle: OutputManagerHandle,
|
||
|
|
|
||
|
|
/// Event loop proxy to wake it up.
|
||
|
|
event_loop_awakener: calloop::ping::Ping,
|
||
|
|
|
||
|
|
/// Fullscreen state.
|
||
|
|
fullscreen: Arc<AtomicBool>,
|
||
|
|
|
||
|
|
/// Available windowing features.
|
||
|
|
windowing_features: WindowingFeatures,
|
||
|
|
|
||
|
|
/// Requests that SCTK window should perform.
|
||
|
|
window_requests: Arc<Mutex<Vec<WindowRequest>>>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Window {
|
||
|
|
pub fn new<T>(
|
||
|
|
event_loop_window_target: &EventLoopWindowTarget<T>,
|
||
|
|
attributes: WindowAttributes,
|
||
|
|
platform_attributes: PlatformAttributes,
|
||
|
|
) -> Result<Self, RootOsError> {
|
||
|
|
let surface = event_loop_window_target
|
||
|
|
.env
|
||
|
|
.create_surface_with_scale_callback(move |scale, surface, mut dispatch_data| {
|
||
|
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||
|
|
|
||
|
|
// Get the window that receiced the event.
|
||
|
|
let window_id = super::make_wid(&surface);
|
||
|
|
let mut window_update = winit_state.window_updates.get_mut(&window_id).unwrap();
|
||
|
|
|
||
|
|
// Set pending scale factor.
|
||
|
|
window_update.scale_factor = Some(scale);
|
||
|
|
window_update.redraw_requested = true;
|
||
|
|
|
||
|
|
surface.set_buffer_scale(scale);
|
||
|
|
})
|
||
|
|
.detach();
|
||
|
|
|
||
|
|
let scale_factor = sctk::get_surface_scale_factor(&surface);
|
||
|
|
|
||
|
|
let window_id = super::make_wid(&surface);
|
||
|
|
let fullscreen = Arc::new(AtomicBool::new(false));
|
||
|
|
let fullscreen_clone = fullscreen.clone();
|
||
|
|
|
||
|
|
let (width, height) = attributes
|
||
|
|
.inner_size
|
||
|
|
.map(|size| size.to_logical::<f64>(scale_factor as f64).into())
|
||
|
|
.unwrap_or((800, 600));
|
||
|
|
|
||
|
|
let theme_manager = event_loop_window_target.theme_manager.clone();
|
||
|
|
let mut window = event_loop_window_target
|
||
|
|
.env
|
||
|
|
.create_window::<ConceptFrame, _>(
|
||
|
|
surface.clone(),
|
||
|
|
Some(theme_manager),
|
||
|
|
(width, height),
|
||
|
|
move |event, mut dispatch_data| {
|
||
|
|
use sctk::window::{Event, State};
|
||
|
|
|
||
|
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||
|
|
let mut window_update = winit_state.window_updates.get_mut(&window_id).unwrap();
|
||
|
|
|
||
|
|
match event {
|
||
|
|
Event::Refresh => {
|
||
|
|
window_update.refresh_frame = true;
|
||
|
|
}
|
||
|
|
Event::Configure { new_size, states } => {
|
||
|
|
let is_fullscreen = states.contains(&State::Fullscreen);
|
||
|
|
fullscreen_clone.store(is_fullscreen, Ordering::Relaxed);
|
||
|
|
|
||
|
|
window_update.refresh_frame = true;
|
||
|
|
window_update.redraw_requested = true;
|
||
|
|
if let Some((w, h)) = new_size {
|
||
|
|
window_update.size = Some(LogicalSize::new(w, h));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Event::Close => {
|
||
|
|
window_update.close_window = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
)
|
||
|
|
.map_err(|_| os_error!(OsError::WaylandMisc("failed to create window.")))?;
|
||
|
|
|
||
|
|
// Set decorations.
|
||
|
|
if attributes.decorations {
|
||
|
|
window.set_decorate(Decorations::FollowServer);
|
||
|
|
} else {
|
||
|
|
window.set_decorate(Decorations::None);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Min dimensions.
|
||
|
|
let min_size = attributes
|
||
|
|
.min_inner_size
|
||
|
|
.map(|size| size.to_logical::<f64>(scale_factor as f64).into());
|
||
|
|
window.set_min_size(min_size);
|
||
|
|
|
||
|
|
// Max dimensions.
|
||
|
|
let max_size = attributes
|
||
|
|
.min_inner_size
|
||
|
|
.map(|size| size.to_logical::<f64>(scale_factor as f64).into());
|
||
|
|
window.set_max_size(max_size);
|
||
|
|
|
||
|
|
// Set Wayland specific window attributes.
|
||
|
|
if let Some(app_id) = platform_attributes.app_id {
|
||
|
|
window.set_app_id(app_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set common window attributes.
|
||
|
|
//
|
||
|
|
// We set resizable after other attributes, since it touches min and max size under
|
||
|
|
// the hood.
|
||
|
|
window.set_resizable(attributes.resizable);
|
||
|
|
window.set_title(attributes.title);
|
||
|
|
|
||
|
|
// Set fullscreen/maximized if so was requested.
|
||
|
|
match attributes.fullscreen {
|
||
|
|
Some(Fullscreen::Exclusive(_)) => {
|
||
|
|
warn!("`Fullscreen::Exclusive` is ignored on Wayland")
|
||
|
|
}
|
||
|
|
Some(Fullscreen::Borderless(monitor)) => {
|
||
|
|
let monitor =
|
||
|
|
monitor.and_then(|RootMonitorHandle { inner: monitor }| match monitor {
|
||
|
|
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
||
|
|
#[cfg(feature = "x11")]
|
||
|
|
PlatformMonitorHandle::X(_) => None,
|
||
|
|
});
|
||
|
|
|
||
|
|
window.set_fullscreen(monitor.as_ref());
|
||
|
|
}
|
||
|
|
None => {
|
||
|
|
if attributes.maximized {
|
||
|
|
window.set_maximized();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let size = Arc::new(Mutex::new(LogicalSize::new(width, height)));
|
||
|
|
|
||
|
|
// We should trigger redraw and commit the surface for the newly created window.
|
||
|
|
let mut window_update = WindowUpdate::new();
|
||
|
|
window_update.refresh_frame = true;
|
||
|
|
window_update.redraw_requested = true;
|
||
|
|
|
||
|
|
let window_id = super::make_wid(&surface);
|
||
|
|
let window_requests = Arc::new(Mutex::new(Vec::with_capacity(64)));
|
||
|
|
|
||
|
|
// Create a handle that performs all the requests on underlying sctk a window.
|
||
|
|
let window_handle = WindowHandle::new(window, size.clone(), window_requests.clone());
|
||
|
|
|
||
|
|
let mut winit_state = event_loop_window_target.state.borrow_mut();
|
||
|
|
|
||
|
|
winit_state.window_map.insert(window_id, window_handle);
|
||
|
|
|
||
|
|
winit_state
|
||
|
|
.window_updates
|
||
|
|
.insert(window_id, WindowUpdate::new());
|
||
|
|
|
||
|
|
let windowing_features = event_loop_window_target.windowing_features;
|
||
|
|
|
||
|
|
// Send all updates to the server.
|
||
|
|
let wayland_source = &event_loop_window_target.wayland_source;
|
||
|
|
let event_loop_handle = &event_loop_window_target.event_loop_handle;
|
||
|
|
|
||
|
|
// To make our window usable for drawing right away we must `ack` a `configure`
|
||
|
|
// from the server, the acking part here is done by SCTK window frame, so we just
|
||
|
|
// need to sync with server so it'll be done automatically for us.
|
||
|
|
event_loop_handle.with_source(&wayland_source, |event_queue| {
|
||
|
|
let event_queue = event_queue.queue();
|
||
|
|
let _ = event_queue.sync_roundtrip(&mut *winit_state, |_, _, _| unreachable!());
|
||
|
|
});
|
||
|
|
|
||
|
|
// We all praise GNOME for these 3 lines of pure magic. If we don't do that,
|
||
|
|
// GNOME will shrink our window a bit for the size of the decorations. I guess it
|
||
|
|
// happens because we haven't committed them with buffers to the server.
|
||
|
|
let window_handle = winit_state.window_map.get_mut(&window_id).unwrap();
|
||
|
|
window_handle.window.refresh();
|
||
|
|
|
||
|
|
let output_manager_handle = event_loop_window_target.output_manager.handle();
|
||
|
|
|
||
|
|
let window = Self {
|
||
|
|
window_id,
|
||
|
|
surface,
|
||
|
|
display: event_loop_window_target.display.clone(),
|
||
|
|
output_manager_handle,
|
||
|
|
size,
|
||
|
|
window_requests,
|
||
|
|
event_loop_awakener: event_loop_window_target.event_loop_awakener.clone(),
|
||
|
|
fullscreen,
|
||
|
|
windowing_features,
|
||
|
|
};
|
||
|
|
|
||
|
|
Ok(window)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Window {
|
||
|
|
#[inline]
|
||
|
|
pub fn id(&self) -> WindowId {
|
||
|
|
self.window_id
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_title(&self, title: &str) {
|
||
|
|
let title_request = WindowRequest::Title(title.to_owned());
|
||
|
|
self.window_requests.lock().unwrap().push(title_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_visible(&self, _visible: bool) {
|
||
|
|
// Not possible on Wayland.
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||
|
|
Err(NotSupportedError::new())
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||
|
|
Err(NotSupportedError::new())
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_outer_position(&self, _: Position) {
|
||
|
|
// Not possible on Wayland.
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||
|
|
self.size
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.to_physical(self.scale_factor() as f64)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn request_redraw(&self) {
|
||
|
|
let redraw_request = WindowRequest::Redraw;
|
||
|
|
self.window_requests.lock().unwrap().push(redraw_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||
|
|
self.size
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.to_physical(self.scale_factor() as f64)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_inner_size(&self, size: Size) {
|
||
|
|
let scale_factor = self.scale_factor() as f64;
|
||
|
|
|
||
|
|
let size = size.to_logical::<u32>(scale_factor);
|
||
|
|
*self.size.lock().unwrap() = size;
|
||
|
|
|
||
|
|
let frame_size_request = WindowRequest::FrameSize(size);
|
||
|
|
self.window_requests
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.push(frame_size_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||
|
|
let scale_factor = self.scale_factor() as f64;
|
||
|
|
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
|
||
|
|
|
||
|
|
let min_size_request = WindowRequest::MinSize(size);
|
||
|
|
self.window_requests.lock().unwrap().push(min_size_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||
|
|
let scale_factor = self.scale_factor() as f64;
|
||
|
|
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
|
||
|
|
|
||
|
|
let max_size_request = WindowRequest::MaxSize(size);
|
||
|
|
self.window_requests.lock().unwrap().push(max_size_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_resizable(&self, resizable: bool) {
|
||
|
|
let resizeable_request = WindowRequest::Resizeable(resizable);
|
||
|
|
self.window_requests
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.push(resizeable_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn scale_factor(&self) -> u32 {
|
||
|
|
// The scale factor from `get_surface_scale_factor` is always greater than zero, so
|
||
|
|
// u32 conversion is safe.
|
||
|
|
sctk::get_surface_scale_factor(&self.surface) as u32
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_decorations(&self, decorate: bool) {
|
||
|
|
let decorate_request = WindowRequest::Decorate(decorate);
|
||
|
|
self.window_requests.lock().unwrap().push(decorate_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_minimized(&self, minimized: bool) {
|
||
|
|
// You can't unminimize the window on Wayland.
|
||
|
|
if !minimized {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
let minimize_request = WindowRequest::Minimize;
|
||
|
|
self.window_requests.lock().unwrap().push(minimize_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_maximized(&self, maximized: bool) {
|
||
|
|
let maximize_request = WindowRequest::Maximize(maximized);
|
||
|
|
self.window_requests.lock().unwrap().push(maximize_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
||
|
|
if self.fullscreen.load(Ordering::Relaxed) {
|
||
|
|
let current_monitor = self.current_monitor().map(|monitor| RootMonitorHandle {
|
||
|
|
inner: PlatformMonitorHandle::Wayland(monitor),
|
||
|
|
});
|
||
|
|
|
||
|
|
Some(Fullscreen::Borderless(current_monitor))
|
||
|
|
} else {
|
||
|
|
None
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||
|
|
let fullscreen_request = match fullscreen {
|
||
|
|
Some(Fullscreen::Exclusive(_)) => {
|
||
|
|
warn!("`Fullscreen::Exclusive` is ignored on Wayland");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
Some(Fullscreen::Borderless(monitor)) => {
|
||
|
|
let monitor =
|
||
|
|
monitor.and_then(|RootMonitorHandle { inner: monitor }| match monitor {
|
||
|
|
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
||
|
|
#[cfg(feature = "x11")]
|
||
|
|
PlatformMonitorHandle::X(_) => None,
|
||
|
|
});
|
||
|
|
|
||
|
|
WindowRequest::Fullscreen(monitor)
|
||
|
|
}
|
||
|
|
None => WindowRequest::UnsetFullscreen,
|
||
|
|
};
|
||
|
|
|
||
|
|
self.window_requests
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.push(fullscreen_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_theme<T: Theme>(&self, theme: T) {
|
||
|
|
// First buttons is minimize, then maximize, and then close.
|
||
|
|
let buttons: Vec<(ButtonColorSpec, ButtonColorSpec)> =
|
||
|
|
[Button::Minimize, Button::Maximize, Button::Close]
|
||
|
|
.iter()
|
||
|
|
.map(|button| {
|
||
|
|
let button = *button;
|
||
|
|
let idle_active_bg = theme
|
||
|
|
.button_color(button, ButtonState::Idle, false, true)
|
||
|
|
.into();
|
||
|
|
let idle_inactive_bg = theme
|
||
|
|
.button_color(button, ButtonState::Idle, false, false)
|
||
|
|
.into();
|
||
|
|
let idle_active_icon = theme
|
||
|
|
.button_color(button, ButtonState::Idle, true, true)
|
||
|
|
.into();
|
||
|
|
let idle_inactive_icon = theme
|
||
|
|
.button_color(button, ButtonState::Idle, true, false)
|
||
|
|
.into();
|
||
|
|
let idle_bg = ColorSpec {
|
||
|
|
active: idle_active_bg,
|
||
|
|
inactive: idle_inactive_bg,
|
||
|
|
};
|
||
|
|
let idle_icon = ColorSpec {
|
||
|
|
active: idle_active_icon,
|
||
|
|
inactive: idle_inactive_icon,
|
||
|
|
};
|
||
|
|
|
||
|
|
let hovered_active_bg = theme
|
||
|
|
.button_color(button, ButtonState::Hovered, false, true)
|
||
|
|
.into();
|
||
|
|
let hovered_inactive_bg = theme
|
||
|
|
.button_color(button, ButtonState::Hovered, false, false)
|
||
|
|
.into();
|
||
|
|
let hovered_active_icon = theme
|
||
|
|
.button_color(button, ButtonState::Hovered, true, true)
|
||
|
|
.into();
|
||
|
|
let hovered_inactive_icon = theme
|
||
|
|
.button_color(button, ButtonState::Hovered, true, false)
|
||
|
|
.into();
|
||
|
|
let hovered_bg = ColorSpec {
|
||
|
|
active: hovered_active_bg,
|
||
|
|
inactive: hovered_inactive_bg,
|
||
|
|
};
|
||
|
|
let hovered_icon = ColorSpec {
|
||
|
|
active: hovered_active_icon,
|
||
|
|
inactive: hovered_inactive_icon,
|
||
|
|
};
|
||
|
|
|
||
|
|
let disabled_active_bg = theme
|
||
|
|
.button_color(button, ButtonState::Disabled, false, true)
|
||
|
|
.into();
|
||
|
|
let disabled_inactive_bg = theme
|
||
|
|
.button_color(button, ButtonState::Disabled, false, false)
|
||
|
|
.into();
|
||
|
|
let disabled_active_icon = theme
|
||
|
|
.button_color(button, ButtonState::Disabled, true, true)
|
||
|
|
.into();
|
||
|
|
let disabled_inactive_icon = theme
|
||
|
|
.button_color(button, ButtonState::Disabled, true, false)
|
||
|
|
.into();
|
||
|
|
let disabled_bg = ColorSpec {
|
||
|
|
active: disabled_active_bg,
|
||
|
|
inactive: disabled_inactive_bg,
|
||
|
|
};
|
||
|
|
let disabled_icon = ColorSpec {
|
||
|
|
active: disabled_active_icon,
|
||
|
|
inactive: disabled_inactive_icon,
|
||
|
|
};
|
||
|
|
|
||
|
|
let button_bg = ButtonColorSpec {
|
||
|
|
idle: idle_bg,
|
||
|
|
hovered: hovered_bg,
|
||
|
|
disabled: disabled_bg,
|
||
|
|
};
|
||
|
|
let button_icon = ButtonColorSpec {
|
||
|
|
idle: idle_icon,
|
||
|
|
hovered: hovered_icon,
|
||
|
|
disabled: disabled_icon,
|
||
|
|
};
|
||
|
|
|
||
|
|
(button_icon, button_bg)
|
||
|
|
})
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
let minimize_button = Some(buttons[0]);
|
||
|
|
let maximize_button = Some(buttons[1]);
|
||
|
|
let close_button = Some(buttons[2]);
|
||
|
|
|
||
|
|
// The first color is bar, then separator, and then text color.
|
||
|
|
let titlebar_colors: Vec<ColorSpec> = [Element::Bar, Element::Separator, Element::Text]
|
||
|
|
.iter()
|
||
|
|
.map(|element| {
|
||
|
|
let element = *element;
|
||
|
|
let active = theme.element_color(element, true).into();
|
||
|
|
let inactive = theme.element_color(element, false).into();
|
||
|
|
|
||
|
|
ColorSpec { active, inactive }
|
||
|
|
})
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
let primary_color = titlebar_colors[0];
|
||
|
|
let secondary_color = titlebar_colors[1];
|
||
|
|
let title_color = titlebar_colors[2];
|
||
|
|
|
||
|
|
let title_font = theme.font();
|
||
|
|
|
||
|
|
let concept_config = ConceptConfig {
|
||
|
|
primary_color,
|
||
|
|
secondary_color,
|
||
|
|
title_color,
|
||
|
|
title_font,
|
||
|
|
minimize_button,
|
||
|
|
maximize_button,
|
||
|
|
close_button,
|
||
|
|
};
|
||
|
|
|
||
|
|
let theme_request = WindowRequest::Theme(concept_config);
|
||
|
|
self.window_requests.lock().unwrap().push(theme_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||
|
|
let cursor_icon_request = WindowRequest::NewCursorIcon(cursor);
|
||
|
|
self.window_requests
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.push(cursor_icon_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_cursor_visible(&self, visible: bool) {
|
||
|
|
let cursor_visible_request = WindowRequest::ShowCursor(visible);
|
||
|
|
self.window_requests
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.push(cursor_visible_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
||
|
|
if !self.windowing_features.cursor_grab() {
|
||
|
|
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||
|
|
}
|
||
|
|
|
||
|
|
let cursor_grab_request = WindowRequest::GrabCursor(grab);
|
||
|
|
self.window_requests
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.push(cursor_grab_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_cursor_position(&self, _: Position) -> Result<(), ExternalError> {
|
||
|
|
// XXX This is possible if the locked pointer is being used. We don't have any
|
||
|
|
// API for that right now, but it could be added in
|
||
|
|
// https://github.com/rust-windowing/winit/issues/1677.
|
||
|
|
//
|
||
|
|
// This function is essential for the locked pointer API.
|
||
|
|
//
|
||
|
|
// See pointer-constraints-unstable-v1.xml.
|
||
|
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn set_ime_position(&self, position: Position) {
|
||
|
|
let scale_factor = self.scale_factor() as f64;
|
||
|
|
let position = position.to_logical(scale_factor);
|
||
|
|
let ime_position_request = WindowRequest::IMEPosition(position);
|
||
|
|
self.window_requests
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.push(ime_position_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn display(&self) -> &Display {
|
||
|
|
&self.display
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn surface(&self) -> &WlSurface {
|
||
|
|
&self.surface
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||
|
|
let output = sctk::get_surface_outputs(&self.surface).last()?.clone();
|
||
|
|
Some(MonitorHandle::new(output))
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||
|
|
self.output_manager_handle.available_outputs()
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
||
|
|
None
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline]
|
||
|
|
pub fn raw_window_handle(&self) -> WaylandHandle {
|
||
|
|
let display = self.display.get_display_ptr() as *mut _;
|
||
|
|
let surface = self.surface.as_ref().c_ptr() as *mut _;
|
||
|
|
|
||
|
|
WaylandHandle {
|
||
|
|
display,
|
||
|
|
surface,
|
||
|
|
..WaylandHandle::empty()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl From<LocalARGBColor> for ARGBColor {
|
||
|
|
fn from(color: LocalARGBColor) -> Self {
|
||
|
|
let a = color.a;
|
||
|
|
let r = color.r;
|
||
|
|
let g = color.g;
|
||
|
|
let b = color.b;
|
||
|
|
Self { a, r, g, b }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Drop for Window {
|
||
|
|
fn drop(&mut self) {
|
||
|
|
let close_request = WindowRequest::Close;
|
||
|
|
self.window_requests.lock().unwrap().push(close_request);
|
||
|
|
self.event_loop_awakener.ping();
|
||
|
|
}
|
||
|
|
}
|