mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
feat(cef): implement traffic light positioning (#14660)
* feat(cef): implement traffic light positioning * handle X axis as well
This commit is contained in:
@@ -714,6 +714,7 @@ wrap_window_delegate! {
|
||||
attributes: Arc<RefCell<crate::CefWindowBuilder>>,
|
||||
last_emitted_position: RefCell<PhysicalPosition<i32>>,
|
||||
last_emitted_size: RefCell<PhysicalSize<u32>>,
|
||||
context: Context<T>
|
||||
}
|
||||
|
||||
impl ViewDelegate {
|
||||
@@ -937,6 +938,19 @@ wrap_window_delegate! {
|
||||
if a.visible.unwrap_or(true) {
|
||||
window.show();
|
||||
}
|
||||
|
||||
// Set traffic light position on macOS after window is fully created
|
||||
// by posting a task to the UI thread to avoid issues with early setting
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(pos) = a.traffic_light_position {
|
||||
let window_message = WindowMessage::SetTrafficLightPosition(pos);
|
||||
let message = Message::Window {
|
||||
window_id: self.window_id,
|
||||
message: window_message,
|
||||
};
|
||||
|
||||
send_message_task(&self.context, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,6 +1031,11 @@ wrap_window_delegate! {
|
||||
) {
|
||||
let (Some(window), Some(bounds)) = (window, bounds) else { return; };
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(pos) = &self.attributes.borrow().traffic_light_position {
|
||||
apply_traffic_light_position(window.window_handle(), pos);
|
||||
}
|
||||
|
||||
// Update autoresize overlay bounds (moved from on_layout_changed)
|
||||
if let Some(app_window) = self.windows.borrow().get(&self.window_id) {
|
||||
for wrapper in &app_window.webviews {
|
||||
@@ -2335,7 +2354,13 @@ fn handle_window_message<T: UserEvent>(
|
||||
// TODO: Implement title bar style
|
||||
}
|
||||
WindowMessage::SetTrafficLightPosition(_position) => {
|
||||
// TODO: Implement traffic light position
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(app_window) = context.windows.borrow().get(&window_id) {
|
||||
app_window.attributes.borrow_mut().traffic_light_position = Some(_position);
|
||||
if let Some(window) = app_window.window() {
|
||||
apply_traffic_light_position(window.window_handle(), &_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowMessage::SetTheme(_theme) => {
|
||||
// TODO: Implement theme
|
||||
@@ -2608,6 +2633,7 @@ pub(crate) fn create_window<T: UserEvent>(
|
||||
attributes.clone(),
|
||||
RefCell::new(Default::default()),
|
||||
RefCell::new(Default::default()),
|
||||
context.clone(),
|
||||
);
|
||||
|
||||
let window = window_create_top_level(Some(&mut delegate)).expect("Failed to create window");
|
||||
@@ -2664,6 +2690,11 @@ wrap_task! {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_message_task<T: UserEvent>(context: &Context<T>, message: Message<T>) {
|
||||
let mut task = SendMessageTask::new(context.clone(), Arc::new(RefCell::new(message)));
|
||||
cef::post_task(sys::cef_thread_id_t::TID_UI.into(), Some(&mut task));
|
||||
}
|
||||
|
||||
fn send_window_event<T: UserEvent>(
|
||||
window_id: WindowId,
|
||||
windows: &Arc<RefCell<HashMap<WindowId, AppWindow>>>,
|
||||
@@ -3166,3 +3197,50 @@ pub(crate) fn ensure_valid_content_view(
|
||||
// No replacement needed; return the original handle
|
||||
window_handle
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn apply_traffic_light_position(window: *mut std::ffi::c_void, position: &Position) {
|
||||
use objc2::msg_send;
|
||||
use objc2::rc::Retained;
|
||||
use objc2_app_kit::{NSView, NSWindowButton};
|
||||
|
||||
let nsview = unsafe { Retained::<NSView>::retain(window as _) };
|
||||
let Some(nsview) = nsview else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(nswindow) = nsview.window() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(close) = nswindow.standardWindowButton(NSWindowButton::CloseButton) else {
|
||||
return;
|
||||
};
|
||||
let Some(miniaturize) = nswindow.standardWindowButton(NSWindowButton::MiniaturizeButton) else {
|
||||
return;
|
||||
};
|
||||
let Some(zoom) = nswindow.standardWindowButton(NSWindowButton::ZoomButton) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let pos = position.to_logical::<f64>(nswindow.backingScaleFactor() as f64);
|
||||
let (x, y) = (pos.x, pos.y);
|
||||
|
||||
let title_bar_container_view = unsafe { close.superview().unwrap().superview().unwrap() };
|
||||
|
||||
let close_rect = NSView::frame(&close);
|
||||
let title_bar_frame_height = close_rect.size.height + y;
|
||||
let mut title_bar_rect = NSView::frame(&title_bar_container_view);
|
||||
title_bar_rect.size.height = title_bar_frame_height;
|
||||
title_bar_rect.origin.y = nswindow.frame().size.height - title_bar_frame_height;
|
||||
let _: () = unsafe { msg_send![&title_bar_container_view, setFrame: title_bar_rect] };
|
||||
|
||||
let window_buttons = vec![close, miniaturize.clone(), zoom];
|
||||
let space_between = NSView::frame(&miniaturize).origin.x - close_rect.origin.x;
|
||||
|
||||
for (i, button) in window_buttons.into_iter().enumerate() {
|
||||
let mut rect = NSView::frame(&button);
|
||||
rect.origin.x = x + (i as f64 * space_between);
|
||||
unsafe { button.setFrameOrigin(rect.origin) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,10 +859,14 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
|
||||
#[cfg(target_os = "macos")]
|
||||
#[must_use]
|
||||
pub fn traffic_light_position<P: Into<Position>>(mut self, position: P) -> Self {
|
||||
let position = position.into();
|
||||
|
||||
self.window_builder = self.window_builder.traffic_light_position(position.clone());
|
||||
|
||||
self.webview_builder.webview_attributes = self
|
||||
.webview_builder
|
||||
.webview_attributes
|
||||
.traffic_light_position(position.into());
|
||||
.traffic_light_position(position);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2213,6 +2217,15 @@ impl<R: Runtime> WebviewWindow<R> {
|
||||
self.window.set_title_bar_style(style)
|
||||
}
|
||||
|
||||
/// Sets the position of the traffic light buttons. **macOS only**.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS only**.
|
||||
pub fn set_traffic_light_position(&self, position: Position) -> crate::Result<()> {
|
||||
self.window.set_traffic_light_position(position)
|
||||
}
|
||||
|
||||
/// Sets the theme for this window.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
|
||||
@@ -857,6 +857,14 @@ impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`crate::TitleBarStyle`].
|
||||
#[cfg(target_os = "macos")]
|
||||
#[must_use]
|
||||
pub fn traffic_light_position<P: Into<Position>>(mut self, position: P) -> Self {
|
||||
self.window_builder = self.window_builder.traffic_light_position(position);
|
||||
self
|
||||
}
|
||||
|
||||
/// Hide the window title.
|
||||
#[cfg(target_os = "macos")]
|
||||
#[must_use]
|
||||
@@ -2174,6 +2182,19 @@ tauri::Builder::default()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Sets the position of the traffic light buttons. **macOS only**.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS only**.
|
||||
pub fn set_traffic_light_position(&self, position: Position) -> crate::Result<()> {
|
||||
self
|
||||
.window
|
||||
.dispatcher
|
||||
.set_traffic_light_position(position)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Sets the theme for this window.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
|
||||
Reference in New Issue
Block a user