Merge 7f837927f1
into f8667a8379
This commit is contained in:
commit
ef7ae7ca76
20 changed files with 599 additions and 148 deletions
|
@ -1,5 +1,5 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, Context, EventEmitter, IntoElement, PlatformDisplay, Size, Window,
|
App, Context, EventEmitter, IntoElement, LayoutDirection, PlatformDisplay, Size, Window,
|
||||||
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
|
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
|
||||||
linear_color_stop, linear_gradient, point,
|
linear_color_stop, linear_gradient, point,
|
||||||
};
|
};
|
||||||
|
@ -61,6 +61,7 @@ impl AgentNotification {
|
||||||
window_background: WindowBackgroundAppearance::Transparent,
|
window_background: WindowBackgroundAppearance::Transparent,
|
||||||
app_id: Some(app_id.to_owned()),
|
app_id: Some(app_id.to_owned()),
|
||||||
window_min_size: None,
|
window_min_size: None,
|
||||||
|
layout_direction: LayoutDirection::LeftToRight,
|
||||||
window_decorations: Some(WindowDecorations::Client),
|
window_decorations: Some(WindowDecorations::Client),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::{rc::Rc, sync::Arc};
|
||||||
|
|
||||||
pub use collab_panel::CollabPanel;
|
pub use collab_panel::CollabPanel;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
|
App, LayoutDirection, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
|
||||||
WindowDecorations, WindowKind, WindowOptions, point,
|
WindowDecorations, WindowKind, WindowOptions, point,
|
||||||
};
|
};
|
||||||
use panel_settings::MessageEditorSettings;
|
use panel_settings::MessageEditorSettings;
|
||||||
|
@ -64,6 +64,7 @@ fn notification_window_options(
|
||||||
display_id: Some(screen.id()),
|
display_id: Some(screen.id()),
|
||||||
window_background: WindowBackgroundAppearance::Transparent,
|
window_background: WindowBackgroundAppearance::Transparent,
|
||||||
app_id: Some(app_id.to_owned()),
|
app_id: Some(app_id.to_owned()),
|
||||||
|
layout_direction: LayoutDirection::LeftToRight,
|
||||||
window_min_size: None,
|
window_min_size: None,
|
||||||
window_decorations: Some(WindowDecorations::Client),
|
window_decorations: Some(WindowDecorations::Client),
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,3 +310,7 @@ path = "examples/window_shadow.rs"
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "grid_layout"
|
name = "grid_layout"
|
||||||
path = "examples/grid_layout.rs"
|
path = "examples/grid_layout.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "bidi"
|
||||||
|
path = "examples/bidi.rs"
|
149
crates/gpui/examples/bidi.rs
Normal file
149
crates/gpui/examples/bidi.rs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
use gpui::{
|
||||||
|
App, Application, Bounds, Context, KeyBinding, LayoutDirection, Menu, MenuItem, Window,
|
||||||
|
WindowBounds, WindowOptions, actions, div, prelude::*, px, rgb, size,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
struct BidiExampleComponent {
|
||||||
|
header: &'static str,
|
||||||
|
sub_title: &'static str,
|
||||||
|
content: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for BidiExampleComponent {
|
||||||
|
fn render(self, window: &mut Window, _: &mut App) -> impl IntoElement {
|
||||||
|
let main_color = rgb(0xF0F0F3);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.w_full()
|
||||||
|
.gap_2()
|
||||||
|
.child(div().text_3xl().child(self.header))
|
||||||
|
.child(self.sub_title)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.border_r_1()
|
||||||
|
.border_color(main_color)
|
||||||
|
.pr_1()
|
||||||
|
.flex_shrink()
|
||||||
|
.child(self.content),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.border_1()
|
||||||
|
.p_1()
|
||||||
|
.border_color(main_color)
|
||||||
|
.child("Child 1"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.border_1()
|
||||||
|
.p_1()
|
||||||
|
.border_color(main_color)
|
||||||
|
.child("Child 2"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.border_1()
|
||||||
|
.p_1()
|
||||||
|
.border_color(main_color)
|
||||||
|
.child("Child 3"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(div().child(format!(
|
||||||
|
"window.current_layout_direction(): {:?}",
|
||||||
|
window.current_layout_direction()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BidiView;
|
||||||
|
|
||||||
|
impl Render for BidiView {
|
||||||
|
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let main_color = rgb(0xF0F0F3);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.id("bidi-root")
|
||||||
|
.overflow_y_scroll()
|
||||||
|
.bg(rgb(0x0c0c11))
|
||||||
|
.text_color(main_color)
|
||||||
|
.flex()
|
||||||
|
.w_full()
|
||||||
|
.h_full()
|
||||||
|
.flex_col()
|
||||||
|
.p(px(20.0))
|
||||||
|
.gap(px(10.0))
|
||||||
|
.child(BidiExampleComponent {
|
||||||
|
header: "This div uses the window's default window direction!",
|
||||||
|
sub_title: "Try changing layout_direction in the example code's WindowOptions!",
|
||||||
|
content: "This div has a border and padding on its right side, but it's \
|
||||||
|
rendered in RTL, so it shows up on the left instead. Margins are \
|
||||||
|
also automatically switched based on the layout direction.",
|
||||||
|
})
|
||||||
|
.child(div().w_full().dir_ltr().child(BidiExampleComponent {
|
||||||
|
header: "This div is manually set to left-to-right!",
|
||||||
|
sub_title: "Except for the strings, the code for these elements are the exact \
|
||||||
|
as the RTL example! Directionality propagates to child \
|
||||||
|
elements, but you can always set children to a different \
|
||||||
|
directionality with dir_rtl() or dir_ltr().",
|
||||||
|
content: "This div has the border and padding on the right side, and it's \
|
||||||
|
displayed on the right side, as the directionality for the \
|
||||||
|
parent is set to left-to-right.",
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.dir_ltr()
|
||||||
|
.child("If you're on macOS, the menu items are also rendered in RTL."),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Application::new().run(|cx: &mut App| {
|
||||||
|
let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx);
|
||||||
|
// Set the layout direction for anything isolated from a window, for example,
|
||||||
|
// the menu bar on macOS
|
||||||
|
cx.set_default_layout_direction(LayoutDirection::RightToLeft);
|
||||||
|
cx.on_action(quit);
|
||||||
|
cx.bind_keys([KeyBinding::new("secondary-q", Quit, None)]);
|
||||||
|
cx.set_menus(vec![Menu {
|
||||||
|
name: "Bidirectionality Example".into(),
|
||||||
|
items: vec![MenuItem::action("Quit", Quit)],
|
||||||
|
}]);
|
||||||
|
|
||||||
|
cx.open_window(
|
||||||
|
WindowOptions {
|
||||||
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
|
// Set the default layout direction for the window. Setting this to RTL will
|
||||||
|
// cause the window controls to be drawn RTL on supported platforms.
|
||||||
|
|
||||||
|
// NOTE: On macOS, the default window controls are always drawn RTL when the system
|
||||||
|
// language is RTL, and LTR otherwise. When using client-side decorations, as long
|
||||||
|
// as you set titlebar.traffic_light_position, GPUI will not respect this and will
|
||||||
|
// draw the window controls in the layout direction set here.
|
||||||
|
layout_direction: LayoutDirection::RightToLeft,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|_, cx| cx.new(|_| BidiView),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
cx.activate(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Associate actions using the `actions!` macro (or `Action` derive macro)
|
||||||
|
actions!(menu_actions, [Quit]);
|
||||||
|
|
||||||
|
// Define the quit function that is registered with the App
|
||||||
|
fn quit(_: &Quit, cx: &mut App) {
|
||||||
|
println!("Gracefully quitting the application . . .");
|
||||||
|
cx.quit();
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, Application, Bounds, Context, DisplayId, Hsla, Pixels, SharedString, Size, Window,
|
App, Application, Bounds, Context, DisplayId, Hsla, LayoutDirection, Pixels, SharedString,
|
||||||
WindowBackgroundAppearance, WindowBounds, WindowKind, WindowOptions, div, point, prelude::*,
|
Size, Window, WindowBackgroundAppearance, WindowBounds, WindowKind, WindowOptions, div, point,
|
||||||
px, rgb,
|
prelude::*, px, rgb,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WindowContent {
|
struct WindowContent {
|
||||||
|
@ -60,6 +60,7 @@ fn build_window_options(display_id: DisplayId, bounds: Bounds<Pixels>) -> Window
|
||||||
kind: WindowKind::PopUp,
|
kind: WindowKind::PopUp,
|
||||||
is_movable: false,
|
is_movable: false,
|
||||||
app_id: None,
|
app_id: None,
|
||||||
|
layout_direction: LayoutDirection::LeftToRight,
|
||||||
window_min_size: None,
|
window_min_size: None,
|
||||||
window_decorations: None,
|
window_decorations: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,11 @@ use crate::{
|
||||||
Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext, Asset,
|
Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext, Asset,
|
||||||
AssetSource, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DispatchPhase, DisplayId,
|
AssetSource, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DispatchPhase, DisplayId,
|
||||||
EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, KeyContext,
|
EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, KeyContext,
|
||||||
Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
|
Keymap, Keystroke, LayoutDirection, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions,
|
||||||
PlatformDisplay, PlatformKeyboardLayout, Point, PromptBuilder, PromptButton, PromptHandle,
|
Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, Point, PromptBuilder, PromptButton,
|
||||||
PromptLevel, Render, RenderImage, RenderablePromptHandle, Reservation, ScreenCaptureSource,
|
PromptHandle, PromptLevel, Render, RenderImage, RenderablePromptHandle, Reservation,
|
||||||
SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance,
|
ScreenCaptureSource, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window,
|
||||||
WindowHandle, WindowId, WindowInvalidator,
|
WindowAppearance, WindowHandle, WindowId, WindowInvalidator,
|
||||||
colors::{Colors, GlobalColors},
|
colors::{Colors, GlobalColors},
|
||||||
current_platform, hash, init_app_menus,
|
current_platform, hash, init_app_menus,
|
||||||
};
|
};
|
||||||
|
@ -1539,6 +1539,17 @@ impl App {
|
||||||
self.platform.get_menus()
|
self.platform.get_menus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the default layout direction for elements that are not tied to a window,
|
||||||
|
/// for example, menus on macOS
|
||||||
|
pub fn set_default_layout_direction(&self, direction: LayoutDirection) {
|
||||||
|
self.platform.set_default_layout_direction(direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the default layout direction for elements that are not tied to a window
|
||||||
|
pub fn get_default_layout_direction(&self) -> LayoutDirection {
|
||||||
|
self.platform.get_default_layout_direction()
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the right click menu for the app icon in the dock
|
/// Sets the right click menu for the app icon in the dock
|
||||||
pub fn set_dock_menu(&self, menus: Vec<MenuItem>) {
|
pub fn set_dock_menu(&self, menus: Vec<MenuItem>) {
|
||||||
self.platform.set_dock_menu(menus, &self.keymap.borrow())
|
self.platform.set_dock_menu(menus, &self.keymap.borrow())
|
||||||
|
|
|
@ -1294,13 +1294,15 @@ impl Element for Div {
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|style, window, cx| {
|
|style, window, cx| {
|
||||||
window.with_text_style(style.text_style().cloned(), |window| {
|
window.with_bidi_style(style.bidi_style().cloned(), |window| {
|
||||||
child_layout_ids = self
|
window.with_text_style(style.text_style().cloned(), |window| {
|
||||||
.children
|
child_layout_ids = self
|
||||||
.iter_mut()
|
.children
|
||||||
.map(|child| child.request_layout(window, cx))
|
.iter_mut()
|
||||||
.collect::<SmallVec<_>>();
|
.map(|child| child.request_layout(window, cx))
|
||||||
window.request_layout(style, child_layout_ids.iter().copied(), cx)
|
.collect::<SmallVec<_>>();
|
||||||
|
window.request_layout(style, child_layout_ids.iter().copied(), cx)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1616,22 +1618,24 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.with_text_style(style.text_style().cloned(), |window| {
|
window.with_bidi_style(style.bidi_style().cloned(), |window| {
|
||||||
window.with_content_mask(
|
window.with_text_style(style.text_style().cloned(), |window| {
|
||||||
style.overflow_mask(bounds, window.rem_size()),
|
window.with_content_mask(
|
||||||
|window| {
|
style.overflow_mask(bounds, window.rem_size()),
|
||||||
let hitbox = if self.should_insert_hitbox(&style, window, cx) {
|
|window| {
|
||||||
Some(window.insert_hitbox(bounds, self.hitbox_behavior))
|
let hitbox = if self.should_insert_hitbox(&style, window, cx) {
|
||||||
} else {
|
Some(window.insert_hitbox(bounds, self.hitbox_behavior))
|
||||||
None
|
} else {
|
||||||
};
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let scroll_offset =
|
let scroll_offset =
|
||||||
self.clamp_scroll_position(bounds, &style, window, cx);
|
self.clamp_scroll_position(bounds, &style, window, cx);
|
||||||
let result = f(&style, scroll_offset, hitbox, window, cx);
|
let result = f(&style, scroll_offset, hitbox, window, cx);
|
||||||
(result, element_state)
|
(result, element_state)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1763,61 +1767,65 @@ impl Interactivity {
|
||||||
|
|
||||||
window.with_element_opacity(style.opacity, |window| {
|
window.with_element_opacity(style.opacity, |window| {
|
||||||
style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
|
style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
|
||||||
window.with_text_style(style.text_style().cloned(), |window| {
|
window.with_bidi_style(style.bidi_style().cloned(), |window| {
|
||||||
window.with_content_mask(
|
window.with_text_style(style.text_style().cloned(), |window| {
|
||||||
style.overflow_mask(bounds, window.rem_size()),
|
window.with_content_mask(
|
||||||
|window| {
|
style.overflow_mask(bounds, window.rem_size()),
|
||||||
if let Some(hitbox) = hitbox {
|
|window| {
|
||||||
#[cfg(debug_assertions)]
|
if let Some(hitbox) = hitbox {
|
||||||
self.paint_debug_info(
|
#[cfg(debug_assertions)]
|
||||||
global_id, hitbox, &style, window, cx,
|
self.paint_debug_info(
|
||||||
);
|
global_id, hitbox, &style, window, cx,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(drag) = cx.active_drag.as_ref() {
|
if let Some(drag) = cx.active_drag.as_ref() {
|
||||||
if let Some(mouse_cursor) = drag.cursor_style {
|
if let Some(mouse_cursor) = drag.cursor_style {
|
||||||
window.set_window_cursor_style(mouse_cursor);
|
window.set_window_cursor_style(mouse_cursor);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(mouse_cursor) = style.mouse_cursor {
|
||||||
|
window.set_cursor_style(mouse_cursor, hitbox);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if let Some(mouse_cursor) = style.mouse_cursor {
|
if let Some(group) = self.group.clone() {
|
||||||
window.set_cursor_style(mouse_cursor, hitbox);
|
GroupHitboxes::push(group, hitbox.id, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(area) = self.window_control {
|
||||||
|
window.insert_window_control_hitbox(
|
||||||
|
area,
|
||||||
|
hitbox.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.paint_mouse_listeners(
|
||||||
|
hitbox,
|
||||||
|
element_state.as_mut(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
self.paint_scroll_listener(hitbox, &style, window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.paint_keyboard_listeners(window, cx);
|
||||||
|
f(&style, window, cx);
|
||||||
|
|
||||||
|
if let Some(_hitbox) = hitbox {
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
window.insert_inspector_hitbox(
|
||||||
|
_hitbox.id,
|
||||||
|
_inspector_id,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(group) = self.group.as_ref() {
|
||||||
|
GroupHitboxes::pop(group, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
if let Some(group) = self.group.clone() {
|
);
|
||||||
GroupHitboxes::push(group, hitbox.id, cx);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(area) = self.window_control {
|
|
||||||
window
|
|
||||||
.insert_window_control_hitbox(area, hitbox.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.paint_mouse_listeners(
|
|
||||||
hitbox,
|
|
||||||
element_state.as_mut(),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
self.paint_scroll_listener(hitbox, &style, window, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.paint_keyboard_listeners(window, cx);
|
|
||||||
f(&style, window, cx);
|
|
||||||
|
|
||||||
if let Some(_hitbox) = hitbox {
|
|
||||||
#[cfg(any(feature = "inspector", debug_assertions))]
|
|
||||||
window.insert_inspector_hitbox(
|
|
||||||
_hitbox.id,
|
|
||||||
_inspector_id,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(group) = self.group.as_ref() {
|
|
||||||
GroupHitboxes::pop(group, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1933,15 +1941,17 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.with_text_style(
|
window.with_bidi_style(None, |window| {
|
||||||
Some(crate::TextStyleRefinement {
|
window.with_text_style(
|
||||||
color: Some(crate::red()),
|
Some(crate::TextStyleRefinement {
|
||||||
line_height: Some(FONT_SIZE.into()),
|
color: Some(crate::red()),
|
||||||
background_color: Some(crate::white()),
|
line_height: Some(FONT_SIZE.into()),
|
||||||
..Default::default()
|
background_color: Some(crate::white()),
|
||||||
}),
|
..Default::default()
|
||||||
render_debug_text,
|
}),
|
||||||
)
|
render_debug_text,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2497,7 +2507,14 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
style
|
window.with_bidi_style(self.base_style.bidi.clone(), |window| {
|
||||||
|
let layout_direction = window.current_layout_direction();
|
||||||
|
let flex_mapped_style = layout_direction.apply_flex_direction(style);
|
||||||
|
let spacing_mapped_style = layout_direction.apply_spacing_direction(flex_mapped_style);
|
||||||
|
let border_mapped_style = layout_direction.apply_border_direction(spacing_mapped_style);
|
||||||
|
|
||||||
|
border_mapped_style
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -442,11 +442,14 @@ impl TextLayout {
|
||||||
let line_height = element_state.line_height;
|
let line_height = element_state.line_height;
|
||||||
let mut line_origin = bounds.origin;
|
let mut line_origin = bounds.origin;
|
||||||
let text_style = window.text_style();
|
let text_style = window.text_style();
|
||||||
|
let text_align = window
|
||||||
|
.current_layout_direction()
|
||||||
|
.apply_text_align_direction(text_style.text_align);
|
||||||
for line in &element_state.lines {
|
for line in &element_state.lines {
|
||||||
line.paint_background(
|
line.paint_background(
|
||||||
line_origin,
|
line_origin,
|
||||||
line_height,
|
line_height,
|
||||||
text_style.text_align,
|
text_align,
|
||||||
Some(bounds),
|
Some(bounds),
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -455,7 +458,7 @@ impl TextLayout {
|
||||||
line.paint(
|
line.paint(
|
||||||
line_origin,
|
line_origin,
|
||||||
line_height,
|
line_height,
|
||||||
text_style.text_align,
|
text_align,
|
||||||
Some(bounds),
|
Some(bounds),
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -38,10 +38,10 @@ pub(crate) mod scap_screen_capture;
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
|
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
|
||||||
DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
|
DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
|
||||||
ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput,
|
ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LayoutDirection, LineLayout,
|
||||||
Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
|
Pixels, PlatformInput, Point, RenderGlyphParams, RenderImage, RenderImageParams,
|
||||||
ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window,
|
RenderSvgParams, ScaledPixels, Scene, ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer,
|
||||||
WindowControlArea, hash, point, px, size,
|
SvgSize, Task, TaskLabel, Window, WindowControlArea, hash, point, px, size,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_task::Runnable;
|
use async_task::Runnable;
|
||||||
|
@ -238,6 +238,9 @@ pub(crate) trait Platform: 'static {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_default_layout_direction(&self, direction: LayoutDirection);
|
||||||
|
fn get_default_layout_direction(&self) -> LayoutDirection;
|
||||||
|
|
||||||
fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap);
|
fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap);
|
||||||
fn perform_dock_menu_action(&self, _action: usize) {}
|
fn perform_dock_menu_action(&self, _action: usize) {}
|
||||||
fn add_recent_document(&self, _path: &Path) {}
|
fn add_recent_document(&self, _path: &Path) {}
|
||||||
|
@ -1105,6 +1108,9 @@ pub struct WindowOptions {
|
||||||
/// Whether to use client or server side decorations. Wayland only
|
/// Whether to use client or server side decorations. Wayland only
|
||||||
/// Note that this may be ignored.
|
/// Note that this may be ignored.
|
||||||
pub window_decorations: Option<WindowDecorations>,
|
pub window_decorations: Option<WindowDecorations>,
|
||||||
|
|
||||||
|
/// The layout direction
|
||||||
|
pub layout_direction: LayoutDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The variables that can be configured when creating a new window
|
/// The variables that can be configured when creating a new window
|
||||||
|
@ -1144,6 +1150,9 @@ pub(crate) struct WindowParams {
|
||||||
pub display_id: Option<DisplayId>,
|
pub display_id: Option<DisplayId>,
|
||||||
|
|
||||||
pub window_min_size: Option<Size<Pixels>>,
|
pub window_min_size: Option<Size<Pixels>>,
|
||||||
|
|
||||||
|
#[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
|
||||||
|
pub layout_direction: LayoutDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the status of how a window should be opened.
|
/// Represents the status of how a window should be opened.
|
||||||
|
@ -1194,6 +1203,7 @@ impl Default for WindowOptions {
|
||||||
app_id: None,
|
app_id: None,
|
||||||
window_min_size: None,
|
window_min_size: None,
|
||||||
window_decorations: None,
|
window_decorations: None,
|
||||||
|
layout_direction: LayoutDirection::LeftToRight,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ use xkbcommon::xkb::{self, Keycode, Keysym, State};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
||||||
ForegroundExecutor, Keymap, LinuxDispatcher, Menu, MenuItem, OwnedMenu, PathPromptOptions,
|
ForegroundExecutor, Keymap, LayoutDirection, LinuxDispatcher, Menu, MenuItem, OwnedMenu,
|
||||||
Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow,
|
PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout,
|
||||||
Point, Result, Task, WindowAppearance, WindowParams, px,
|
PlatformTextSystem, PlatformWindow, Point, Result, Task, WindowAppearance, WindowParams, px,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "wayland", feature = "x11"))]
|
#[cfg(any(feature = "wayland", feature = "x11"))]
|
||||||
|
@ -95,6 +95,7 @@ pub(crate) struct LinuxCommon {
|
||||||
pub(crate) callbacks: PlatformHandlers,
|
pub(crate) callbacks: PlatformHandlers,
|
||||||
pub(crate) signal: LoopSignal,
|
pub(crate) signal: LoopSignal,
|
||||||
pub(crate) menus: Vec<OwnedMenu>,
|
pub(crate) menus: Vec<OwnedMenu>,
|
||||||
|
pub(crate) default_layout_direction: LayoutDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinuxCommon {
|
impl LinuxCommon {
|
||||||
|
@ -120,6 +121,7 @@ impl LinuxCommon {
|
||||||
auto_hide_scrollbars: false,
|
auto_hide_scrollbars: false,
|
||||||
callbacks,
|
callbacks,
|
||||||
signal,
|
signal,
|
||||||
|
default_layout_direction: LayoutDirection::RightToLeft,
|
||||||
menus: Vec::new(),
|
menus: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -563,6 +565,16 @@ impl<P: LinuxClient + 'static> Platform for P {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_recent_document(&self, _path: &Path) {}
|
fn add_recent_document(&self, _path: &Path) {}
|
||||||
|
|
||||||
|
fn set_default_layout_direction(&self, direction: LayoutDirection) {
|
||||||
|
self.with_common(|common| {
|
||||||
|
common.default_layout_direction = direction;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_layout_direction(&self) -> LayoutDirection {
|
||||||
|
self.with_common(|common| common.default_layout_direction)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "wayland", feature = "x11"))]
|
#[cfg(any(feature = "wayland", feature = "x11"))]
|
||||||
|
|
|
@ -6,9 +6,9 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardEntry, ClipboardItem, ClipboardString,
|
Action, AnyWindowHandle, BackgroundExecutor, ClipboardEntry, ClipboardItem, ClipboardString,
|
||||||
CursorStyle, ForegroundExecutor, Image, ImageFormat, KeyContext, Keymap, MacDispatcher,
|
CursorStyle, ForegroundExecutor, Image, ImageFormat, KeyContext, Keymap, LayoutDirection,
|
||||||
MacDisplay, MacWindow, Menu, MenuItem, OsMenu, OwnedMenu, PathPromptOptions, Platform,
|
MacDispatcher, MacDisplay, MacWindow, Menu, MenuItem, OsMenu, OwnedMenu, PathPromptOptions,
|
||||||
PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow, Result,
|
Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow, Result,
|
||||||
SemanticVersion, SystemMenuType, Task, WindowAppearance, WindowParams, hash,
|
SemanticVersion, SystemMenuType, Task, WindowAppearance, WindowParams, hash,
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, anyhow};
|
use anyhow::{Context as _, anyhow};
|
||||||
|
@ -171,6 +171,7 @@ pub(crate) struct MacPlatformState {
|
||||||
finish_launching: Option<Box<dyn FnOnce()>>,
|
finish_launching: Option<Box<dyn FnOnce()>>,
|
||||||
dock_menu: Option<id>,
|
dock_menu: Option<id>,
|
||||||
menus: Option<Vec<OwnedMenu>>,
|
menus: Option<Vec<OwnedMenu>>,
|
||||||
|
default_layout_direction: LayoutDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MacPlatform {
|
impl Default for MacPlatform {
|
||||||
|
@ -209,6 +210,7 @@ impl MacPlatform {
|
||||||
dock_menu: None,
|
dock_menu: None,
|
||||||
on_keyboard_layout_change: None,
|
on_keyboard_layout_change: None,
|
||||||
menus: None,
|
menus: None,
|
||||||
|
default_layout_direction: LayoutDirection::LeftToRight,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,16 +234,21 @@ impl MacPlatform {
|
||||||
delegate: id,
|
delegate: id,
|
||||||
actions: &mut Vec<Box<dyn Action>>,
|
actions: &mut Vec<Box<dyn Action>>,
|
||||||
keymap: &Keymap,
|
keymap: &Keymap,
|
||||||
|
layout_direction: LayoutDirection,
|
||||||
) -> id {
|
) -> id {
|
||||||
|
let layout_direction = Self::ns_user_interface_layout_direction(layout_direction);
|
||||||
unsafe {
|
unsafe {
|
||||||
let application_menu = NSMenu::new(nil).autorelease();
|
let application_menu = NSMenu::new(nil).autorelease();
|
||||||
application_menu.setDelegate_(delegate);
|
application_menu.setDelegate_(delegate);
|
||||||
|
let _: () =
|
||||||
|
msg_send![application_menu, setUserInterfaceLayoutDirection: layout_direction];
|
||||||
|
|
||||||
for menu_config in menus {
|
for menu_config in menus {
|
||||||
let menu = NSMenu::new(nil).autorelease();
|
let menu = NSMenu::new(nil).autorelease();
|
||||||
let menu_title = ns_string(&menu_config.name);
|
let menu_title = ns_string(&menu_config.name);
|
||||||
menu.setTitle_(menu_title);
|
menu.setTitle_(menu_title);
|
||||||
menu.setDelegate_(delegate);
|
menu.setDelegate_(delegate);
|
||||||
|
let _: () = msg_send![menu, setUserInterfaceLayoutDirection: layout_direction];
|
||||||
|
|
||||||
for item_config in &menu_config.items {
|
for item_config in &menu_config.items {
|
||||||
menu.addItem_(Self::create_menu_item(
|
menu.addItem_(Self::create_menu_item(
|
||||||
|
@ -446,6 +453,13 @@ impl MacPlatform {
|
||||||
version.patchVersion as usize,
|
version.patchVersion as usize,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ns_user_interface_layout_direction(layout_direction: LayoutDirection) -> usize {
|
||||||
|
match layout_direction {
|
||||||
|
LayoutDirection::LeftToRight => 0,
|
||||||
|
LayoutDirection::RightToLeft => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Platform for MacPlatform {
|
impl Platform for MacPlatform {
|
||||||
|
@ -894,8 +908,15 @@ impl Platform for MacPlatform {
|
||||||
unsafe {
|
unsafe {
|
||||||
let app: id = msg_send![APP_CLASS, sharedApplication];
|
let app: id = msg_send![APP_CLASS, sharedApplication];
|
||||||
let mut state = self.0.lock();
|
let mut state = self.0.lock();
|
||||||
|
let layout_direction = state.default_layout_direction;
|
||||||
let actions = &mut state.menu_actions;
|
let actions = &mut state.menu_actions;
|
||||||
let menu = self.create_menu_bar(&menus, NSWindow::delegate(app), actions, keymap);
|
let menu = self.create_menu_bar(
|
||||||
|
&menus,
|
||||||
|
NSWindow::delegate(app),
|
||||||
|
actions,
|
||||||
|
keymap,
|
||||||
|
layout_direction,
|
||||||
|
);
|
||||||
drop(state);
|
drop(state);
|
||||||
app.setMainMenu_(menu);
|
app.setMainMenu_(menu);
|
||||||
}
|
}
|
||||||
|
@ -1219,6 +1240,16 @@ impl Platform for MacPlatform {
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_default_layout_direction(&self, direction: LayoutDirection) {
|
||||||
|
let mut state = self.0.lock();
|
||||||
|
state.default_layout_direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_layout_direction(&self) -> LayoutDirection {
|
||||||
|
let state = self.0.lock();
|
||||||
|
state.default_layout_direction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MacPlatform {
|
impl MacPlatform {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};
|
use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, Bounds, Capslock, DisplayLink, ExternalPaths, FileDropEvent,
|
AnyWindowHandle, Bounds, Capslock, DisplayLink, ExternalPaths, FileDropEvent,
|
||||||
ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
|
ForegroundExecutor, KeyDownEvent, Keystroke, LayoutDirection, Modifiers, ModifiersChangedEvent,
|
||||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
|
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
|
||||||
PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions,
|
PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel,
|
||||||
ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
|
RequestFrameOptions, ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance,
|
||||||
WindowControlArea, WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size,
|
WindowBounds, WindowControlArea, WindowKind, WindowParams, platform::PlatformInputHandler,
|
||||||
|
point, px, size,
|
||||||
};
|
};
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
|
@ -367,6 +368,7 @@ struct MacWindowState {
|
||||||
last_key_equivalent: Option<KeyDownEvent>,
|
last_key_equivalent: Option<KeyDownEvent>,
|
||||||
synthetic_drag_counter: usize,
|
synthetic_drag_counter: usize,
|
||||||
traffic_light_position: Option<Point<Pixels>>,
|
traffic_light_position: Option<Point<Pixels>>,
|
||||||
|
layout_direction: LayoutDirection,
|
||||||
transparent_titlebar: bool,
|
transparent_titlebar: bool,
|
||||||
previous_modifiers_changed_event: Option<PlatformInput>,
|
previous_modifiers_changed_event: Option<PlatformInput>,
|
||||||
keystroke_for_do_command: Option<Keystroke>,
|
keystroke_for_do_command: Option<Keystroke>,
|
||||||
|
@ -406,13 +408,24 @@ impl MacWindowState {
|
||||||
let mut min_button_frame: CGRect = msg_send![min_button, frame];
|
let mut min_button_frame: CGRect = msg_send![min_button, frame];
|
||||||
let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
|
let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
|
||||||
let mut origin = point(
|
let mut origin = point(
|
||||||
traffic_light_position.x,
|
match self.layout_direction {
|
||||||
|
LayoutDirection::LeftToRight => traffic_light_position.x,
|
||||||
|
LayoutDirection::RightToLeft => {
|
||||||
|
self.window_bounds().get_bounds().size.width
|
||||||
|
- traffic_light_position.x
|
||||||
|
- px(close_button_frame.size.width as f32)
|
||||||
|
}
|
||||||
|
},
|
||||||
titlebar_height
|
titlebar_height
|
||||||
- traffic_light_position.y
|
- traffic_light_position.y
|
||||||
- px(close_button_frame.size.height as f32),
|
- px(close_button_frame.size.height as f32),
|
||||||
);
|
);
|
||||||
let button_spacing =
|
let button_spacing =
|
||||||
px((min_button_frame.origin.x - close_button_frame.origin.x) as f32);
|
px(((min_button_frame.origin.x - close_button_frame.origin.x) as f32).abs())
|
||||||
|
* match self.layout_direction {
|
||||||
|
LayoutDirection::LeftToRight => 1.,
|
||||||
|
LayoutDirection::RightToLeft => -1.,
|
||||||
|
};
|
||||||
|
|
||||||
close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
|
close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
|
||||||
let _: () = msg_send![close_button, setFrame: close_button_frame];
|
let _: () = msg_send![close_button, setFrame: close_button_frame];
|
||||||
|
@ -534,6 +547,7 @@ impl MacWindow {
|
||||||
show,
|
show,
|
||||||
display_id,
|
display_id,
|
||||||
window_min_size,
|
window_min_size,
|
||||||
|
layout_direction,
|
||||||
}: WindowParams,
|
}: WindowParams,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
renderer_context: renderer::Context,
|
renderer_context: renderer::Context,
|
||||||
|
@ -660,6 +674,7 @@ impl MacWindow {
|
||||||
external_files_dragged: false,
|
external_files_dragged: false,
|
||||||
first_mouse: false,
|
first_mouse: false,
|
||||||
fullscreen_restore_bounds: Bounds::default(),
|
fullscreen_restore_bounds: Bounds::default(),
|
||||||
|
layout_direction: layout_direction,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
(*native_window).set_ivar(
|
(*native_window).set_ivar(
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
|
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
|
||||||
ForegroundExecutor, Keymap, NoopTextSystem, Platform, PlatformDisplay, PlatformKeyboardLayout,
|
ForegroundExecutor, Keymap, LayoutDirection, NoopTextSystem, Platform, PlatformDisplay,
|
||||||
PlatformTextSystem, PromptButton, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream,
|
PlatformKeyboardLayout, PlatformTextSystem, PromptButton, ScreenCaptureFrame,
|
||||||
SourceMetadata, Task, TestDisplay, TestWindow, WindowAppearance, WindowParams, size,
|
ScreenCaptureSource, ScreenCaptureStream, SourceMetadata, Task, TestDisplay, TestWindow,
|
||||||
|
WindowAppearance, WindowParams, size,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
|
@ -38,6 +39,7 @@ pub(crate) struct TestPlatform {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
bitmap_factory: std::mem::ManuallyDrop<IWICImagingFactory>,
|
bitmap_factory: std::mem::ManuallyDrop<IWICImagingFactory>,
|
||||||
weak: Weak<Self>,
|
weak: Weak<Self>,
|
||||||
|
default_layout_direction: RefCell<LayoutDirection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -119,6 +121,7 @@ impl TestPlatform {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
bitmap_factory,
|
bitmap_factory,
|
||||||
text_system,
|
text_system,
|
||||||
|
default_layout_direction: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,6 +429,14 @@ impl Platform for TestPlatform {
|
||||||
fn open_with_system(&self, _path: &Path) {
|
fn open_with_system(&self, _path: &Path) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_default_layout_direction(&self, direction: LayoutDirection) {
|
||||||
|
*self.default_layout_direction.borrow_mut() = direction
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_layout_direction(&self) -> LayoutDirection {
|
||||||
|
*self.default_layout_direction.borrow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestScreenCaptureSource {
|
impl TestScreenCaptureSource {
|
||||||
|
|
|
@ -54,6 +54,7 @@ pub(crate) struct WindowsPlatformState {
|
||||||
jump_list: JumpList,
|
jump_list: JumpList,
|
||||||
// NOTE: standard cursor handles don't need to close.
|
// NOTE: standard cursor handles don't need to close.
|
||||||
pub(crate) current_cursor: Option<HCURSOR>,
|
pub(crate) current_cursor: Option<HCURSOR>,
|
||||||
|
default_layout_direction: LayoutDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -77,6 +78,7 @@ impl WindowsPlatformState {
|
||||||
callbacks,
|
callbacks,
|
||||||
jump_list,
|
jump_list,
|
||||||
current_cursor,
|
current_cursor,
|
||||||
|
default_layout_direction: LayoutDirection::RightToLeft,
|
||||||
menus: Vec::new(),
|
menus: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -707,6 +709,14 @@ impl Platform for WindowsPlatform {
|
||||||
) -> Vec<SmallVec<[PathBuf; 2]>> {
|
) -> Vec<SmallVec<[PathBuf; 2]>> {
|
||||||
self.update_jump_list(menus, entries)
|
self.update_jump_list(menus, entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_default_layout_direction(&self, direction: LayoutDirection) {
|
||||||
|
self.state.borrow_mut().default_layout_direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_layout_direction(&self) -> LayoutDirection {
|
||||||
|
self.state.borrow().default_layout_direction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for WindowsPlatform {
|
impl Drop for WindowsPlatform {
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use ::util::ResultExt;
|
||||||
|
use anyhow::{Context as _, Result};
|
||||||
|
use async_task::Runnable;
|
||||||
|
use futures::channel::oneshot::{self, Receiver};
|
||||||
|
use raw_window_handle as rwh;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
num::NonZeroIsize,
|
num::NonZeroIsize,
|
||||||
|
@ -9,13 +17,7 @@ use std::{
|
||||||
sync::{Arc, Once},
|
sync::{Arc, Once},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
use windows::Win32::Graphics::Dwm::{DWMWA_NONCLIENT_RTL_LAYOUT, DwmSetWindowAttribute};
|
||||||
use ::util::ResultExt;
|
|
||||||
use anyhow::{Context as _, Result};
|
|
||||||
use async_task::Runnable;
|
|
||||||
use futures::channel::oneshot::{self, Receiver};
|
|
||||||
use raw_window_handle as rwh;
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use windows::{
|
use windows::{
|
||||||
Win32::{
|
Win32::{
|
||||||
Foundation::*,
|
Foundation::*,
|
||||||
|
@ -26,8 +28,6 @@ use windows::{
|
||||||
core::*,
|
core::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub(crate) struct WindowsWindow(pub Rc<WindowsWindowInner>);
|
pub(crate) struct WindowsWindow(pub Rc<WindowsWindowInner>);
|
||||||
|
|
||||||
pub struct WindowsWindowState {
|
pub struct WindowsWindowState {
|
||||||
|
@ -167,7 +167,7 @@ impl WindowsWindowState {
|
||||||
fn calculate_window_bounds(&self) -> (Bounds<Pixels>, bool) {
|
fn calculate_window_bounds(&self) -> (Bounds<Pixels>, bool) {
|
||||||
let placement = unsafe {
|
let placement = unsafe {
|
||||||
let mut placement = WINDOWPLACEMENT {
|
let mut placement = WINDOWPLACEMENT {
|
||||||
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
length: size_of::<WINDOWPLACEMENT>() as u32,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
GetWindowPlacement(self.hwnd, &mut placement).log_err();
|
GetWindowPlacement(self.hwnd, &mut placement).log_err();
|
||||||
|
@ -438,6 +438,18 @@ impl WindowsWindow {
|
||||||
let this = context.inner.take().unwrap()?;
|
let this = context.inner.take().unwrap()?;
|
||||||
let hwnd = creation_result?;
|
let hwnd = creation_result?;
|
||||||
|
|
||||||
|
if params.layout_direction == LayoutDirection::RightToLeft {
|
||||||
|
let mut true_var = 1_i32;
|
||||||
|
unsafe {
|
||||||
|
DwmSetWindowAttribute(
|
||||||
|
hwnd,
|
||||||
|
DWMWA_NONCLIENT_RTL_LAYOUT,
|
||||||
|
&mut true_var as *mut _ as *mut c_void,
|
||||||
|
size_of::<i32>() as u32,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
register_drag_drop(&this)?;
|
register_drag_drop(&this)?;
|
||||||
configure_dwm_dark_mode(hwnd, appearance);
|
configure_dwm_dark_mode(hwnd, appearance);
|
||||||
this.state.borrow_mut().border_offset.update(hwnd)?;
|
this.state.borrow_mut().border_offset.update(hwnd)?;
|
||||||
|
@ -600,7 +612,7 @@ impl PlatformWindow for WindowsWindow {
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut config = TASKDIALOGCONFIG::default();
|
let mut config = TASKDIALOGCONFIG::default();
|
||||||
config.cbSize = std::mem::size_of::<TASKDIALOGCONFIG>() as _;
|
config.cbSize = size_of::<TASKDIALOGCONFIG>() as _;
|
||||||
config.hwndParent = handle;
|
config.hwndParent = handle;
|
||||||
let title;
|
let title;
|
||||||
let main_icon;
|
let main_icon;
|
||||||
|
@ -1271,7 +1283,7 @@ fn retrieve_window_placement(
|
||||||
border_offset: WindowBorderOffset,
|
border_offset: WindowBorderOffset,
|
||||||
) -> Result<WINDOWPLACEMENT> {
|
) -> Result<WINDOWPLACEMENT> {
|
||||||
let mut placement = WINDOWPLACEMENT {
|
let mut placement = WINDOWPLACEMENT {
|
||||||
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
length: size_of::<WINDOWPLACEMENT>() as u32,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
unsafe { GetWindowPlacement(hwnd, &mut placement)? };
|
unsafe { GetWindowPlacement(hwnd, &mut placement)? };
|
||||||
|
@ -1321,7 +1333,7 @@ fn set_window_composition_attribute(hwnd: HWND, color: Option<Color>, state: u32
|
||||||
let mut data = WINDOWCOMPOSITIONATTRIBDATA {
|
let mut data = WINDOWCOMPOSITIONATTRIBDATA {
|
||||||
attrib: 0x13,
|
attrib: 0x13,
|
||||||
pv_data: &accent as *const _ as *mut _,
|
pv_data: &accent as *const _ as *mut _,
|
||||||
cb_data: std::mem::size_of::<AccentPolicy>(),
|
cb_data: size_of::<AccentPolicy>(),
|
||||||
};
|
};
|
||||||
let _ = set_window_composition_attribute(hwnd, &mut data as *mut _ as _);
|
let _ = set_window_composition_attribute(hwnd, &mut data as *mut _ as _);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter, mem,
|
iter, mem,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
|
@ -254,6 +255,9 @@ pub struct Style {
|
||||||
/// The text style of this element
|
/// The text style of this element
|
||||||
pub text: TextStyleRefinement,
|
pub text: TextStyleRefinement,
|
||||||
|
|
||||||
|
/// The bidi style of this element
|
||||||
|
pub bidi: BidiStyleRefinement,
|
||||||
|
|
||||||
/// The mouse cursor style shown when the mouse pointer is over an element.
|
/// The mouse cursor style shown when the mouse pointer is over an element.
|
||||||
pub mouse_cursor: Option<CursorStyle>,
|
pub mouse_cursor: Option<CursorStyle>,
|
||||||
|
|
||||||
|
@ -496,6 +500,86 @@ impl TextStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The direction of layout.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Default)]
|
||||||
|
pub enum LayoutDirection {
|
||||||
|
/// Left-to-right layout.
|
||||||
|
#[default]
|
||||||
|
LeftToRight,
|
||||||
|
/// Right-to-left layout.
|
||||||
|
RightToLeft,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutDirection {
|
||||||
|
/// Sets the flex direction on a style, given the bidi direction
|
||||||
|
pub fn apply_flex_direction(self, mut style: Style) -> Style {
|
||||||
|
match self {
|
||||||
|
LayoutDirection::LeftToRight => style,
|
||||||
|
LayoutDirection::RightToLeft => {
|
||||||
|
style.flex_direction = style.flex_direction.flip_horizontal();
|
||||||
|
style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the margin and padding orientations on a style, given the bidi direction
|
||||||
|
pub fn apply_spacing_direction(self, mut style: Style) -> Style {
|
||||||
|
match self {
|
||||||
|
LayoutDirection::LeftToRight => style,
|
||||||
|
LayoutDirection::RightToLeft => {
|
||||||
|
style.margin = self.apply_edge_direction(style.margin);
|
||||||
|
style.padding = self.apply_edge_direction(style.padding);
|
||||||
|
style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the border orientations on a style, given the bidi direction
|
||||||
|
pub fn apply_border_direction(self, mut style: Style) -> Style {
|
||||||
|
match self {
|
||||||
|
LayoutDirection::LeftToRight => style,
|
||||||
|
LayoutDirection::RightToLeft => {
|
||||||
|
style.border_widths = self.apply_edge_direction(style.border_widths);
|
||||||
|
style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the flex direction on a TextAlign, given the bidi direction
|
||||||
|
pub fn apply_text_align_direction(self, mut text_align: TextAlign) -> TextAlign {
|
||||||
|
match self {
|
||||||
|
LayoutDirection::LeftToRight => text_align,
|
||||||
|
LayoutDirection::RightToLeft => match text_align {
|
||||||
|
TextAlign::Left => TextAlign::Right,
|
||||||
|
TextAlign::Center => TextAlign::Center,
|
||||||
|
TextAlign::Right => TextAlign::Left,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps a set of edges to the correct orientation based on the bidi direction
|
||||||
|
pub fn apply_edge_direction<T>(self, mut edge: Edges<T>) -> Edges<T>
|
||||||
|
where
|
||||||
|
T: Clone + Debug + Default + PartialEq,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
LayoutDirection::LeftToRight => edge,
|
||||||
|
LayoutDirection::RightToLeft => {
|
||||||
|
std::mem::swap(&mut edge.left, &mut edge.right);
|
||||||
|
edge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The properties used to change the layout direction in GPUI
|
||||||
|
#[derive(Refineable, Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Default)]
|
||||||
|
#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct BidiStyle {
|
||||||
|
/// The direction of layout.
|
||||||
|
pub dir: LayoutDirection,
|
||||||
|
}
|
||||||
|
|
||||||
/// A highlight style to apply, similar to a `TextStyle` except
|
/// A highlight style to apply, similar to a `TextStyle` except
|
||||||
/// for a single font, uniformly sized and spaced text.
|
/// for a single font, uniformly sized and spaced text.
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
@ -555,6 +639,15 @@ impl Style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the bidi style in this element style.
|
||||||
|
pub fn bidi_style(&self) -> Option<&BidiStyleRefinement> {
|
||||||
|
if self.bidi.is_some() {
|
||||||
|
Some(&self.bidi)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the content mask for this element style, based on the given bounds.
|
/// Get the content mask for this element style, based on the given bounds.
|
||||||
/// If the element does not hide its overflow, this will return `None`.
|
/// If the element does not hide its overflow, this will return `None`.
|
||||||
pub fn overflow_mask(
|
pub fn overflow_mask(
|
||||||
|
@ -773,6 +866,7 @@ impl Default for Style {
|
||||||
corner_radii: Corners::default(),
|
corner_radii: Corners::default(),
|
||||||
box_shadow: Default::default(),
|
box_shadow: Default::default(),
|
||||||
text: TextStyleRefinement::default(),
|
text: TextStyleRefinement::default(),
|
||||||
|
bidi: BidiStyleRefinement::default(),
|
||||||
mouse_cursor: None,
|
mouse_cursor: None,
|
||||||
opacity: None,
|
opacity: None,
|
||||||
grid_rows: None,
|
grid_rows: None,
|
||||||
|
@ -1166,6 +1260,18 @@ pub enum FlexDirection {
|
||||||
ColumnReverse,
|
ColumnReverse,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FlexDirection {
|
||||||
|
/// Reverse the direction that items will be drawn in.
|
||||||
|
pub fn flip_horizontal(self) -> Self {
|
||||||
|
match self {
|
||||||
|
FlexDirection::Row => FlexDirection::RowReverse,
|
||||||
|
FlexDirection::Column => FlexDirection::Column,
|
||||||
|
FlexDirection::RowReverse => FlexDirection::Row,
|
||||||
|
FlexDirection::ColumnReverse => FlexDirection::ColumnReverse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// How children overflowing their container should affect layout
|
/// How children overflowing their container should affect layout
|
||||||
///
|
///
|
||||||
/// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should
|
/// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
GridPlacement, Hsla, JustifyContent, Length, SharedString, StrikethroughStyle, StyleRefinement,
|
GridPlacement, Hsla, JustifyContent, Length, SharedString, StrikethroughStyle, StyleRefinement,
|
||||||
TextAlign, TextOverflow, TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems,
|
TextAlign, TextOverflow, TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems,
|
||||||
};
|
};
|
||||||
|
use crate::{BidiStyleRefinement, LayoutDirection};
|
||||||
pub use gpui_macros::{
|
pub use gpui_macros::{
|
||||||
border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods,
|
border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods,
|
||||||
overflow_style_methods, padding_style_methods, position_style_methods,
|
overflow_style_methods, padding_style_methods, position_style_methods,
|
||||||
|
@ -743,6 +744,22 @@ pub trait Styled: Sized {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the drawing direction to LTR
|
||||||
|
fn dir_ltr(mut self) -> Self {
|
||||||
|
self.style().bidi = Some(BidiStyleRefinement {
|
||||||
|
dir: Some(LayoutDirection::LeftToRight),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the drawing direction to LTR
|
||||||
|
fn dir_rtl(mut self) -> Self {
|
||||||
|
self.style().bidi = Some(BidiStyleRefinement {
|
||||||
|
dir: Some(LayoutDirection::RightToLeft),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Draws a debug border around this element.
|
/// Draws a debug border around this element.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn debug(mut self) -> Self {
|
fn debug(mut self) -> Self {
|
||||||
|
|
|
@ -292,7 +292,10 @@ impl ToTaffy<taffy::style::Style> for Style {
|
||||||
align_content: self.align_content.map(|x| x.into()),
|
align_content: self.align_content.map(|x| x.into()),
|
||||||
justify_content: self.justify_content.map(|x| x.into()),
|
justify_content: self.justify_content.map(|x| x.into()),
|
||||||
gap: self.gap.to_taffy(rem_size),
|
gap: self.gap.to_taffy(rem_size),
|
||||||
flex_direction: self.flex_direction.into(),
|
flex_direction: match self.bidi.dir.unwrap_or_default() {
|
||||||
|
crate::LayoutDirection::LeftToRight => self.flex_direction.into(),
|
||||||
|
crate::LayoutDirection::RightToLeft => self.flex_direction.flip_horizontal().into(),
|
||||||
|
},
|
||||||
flex_wrap: self.flex_wrap.into(),
|
flex_wrap: self.flex_wrap.into(),
|
||||||
flex_basis: self.flex_basis.to_taffy(rem_size),
|
flex_basis: self.flex_basis.to_taffy(rem_size),
|
||||||
flex_grow: self.flex_grow,
|
flex_grow: self.flex_grow,
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
use crate::Inspector;
|
use crate::Inspector;
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
|
Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
|
||||||
AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock,
|
AsyncWindowContext, AvailableSpace, Background, BidiStyle, BidiStyleRefinement, BorderStyle,
|
||||||
Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
|
Bounds, BoxShadow, Capslock, Context, Corners, CursorStyle, Decorations, DevicePixels,
|
||||||
DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
|
DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
|
||||||
FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero,
|
EntityId, EventEmitter, FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs,
|
||||||
KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
|
Hsla, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke,
|
||||||
LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent,
|
KeystrokeEvent, LayoutDirection, LayoutId, LineLayoutIndex, Modifiers, ModifiersChangedEvent,
|
||||||
MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||||
PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad,
|
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||||
Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
|
PolychromeSprite, PromptButton, PromptLevel, Quad, Render, RenderGlyphParams, RenderImage,
|
||||||
SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size,
|
RenderImageParams, RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR,
|
||||||
StrikethroughStyle, Style, SubscriberSet, Subscription, TabHandles, TaffyLayoutEngine, Task,
|
SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
|
||||||
TextStyle, TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle,
|
SubscriberSet, Subscription, TabHandles, TaffyLayoutEngine, Task, TextStyle,
|
||||||
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations,
|
TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance,
|
||||||
WindowOptions, WindowParams, WindowTextSystem, point, prelude::*, px, rems, size,
|
WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowOptions,
|
||||||
transparent_black,
|
WindowParams, WindowTextSystem, point, prelude::*, px, rems, size, transparent_black,
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use collections::{FxHashMap, FxHashSet};
|
use collections::{FxHashMap, FxHashSet};
|
||||||
|
@ -835,6 +835,8 @@ pub struct Window {
|
||||||
pub(crate) root: Option<AnyView>,
|
pub(crate) root: Option<AnyView>,
|
||||||
pub(crate) element_id_stack: SmallVec<[ElementId; 32]>,
|
pub(crate) element_id_stack: SmallVec<[ElementId; 32]>,
|
||||||
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
||||||
|
root_layout_direction: LayoutDirection,
|
||||||
|
pub(crate) bidi_style_stack: Vec<BidiStyleRefinement>,
|
||||||
pub(crate) rendered_entity_stack: Vec<EntityId>,
|
pub(crate) rendered_entity_stack: Vec<EntityId>,
|
||||||
pub(crate) element_offset_stack: Vec<Point<Pixels>>,
|
pub(crate) element_offset_stack: Vec<Point<Pixels>>,
|
||||||
pub(crate) element_opacity: Option<f32>,
|
pub(crate) element_opacity: Option<f32>,
|
||||||
|
@ -944,6 +946,7 @@ impl Window {
|
||||||
app_id,
|
app_id,
|
||||||
window_min_size,
|
window_min_size,
|
||||||
window_decorations,
|
window_decorations,
|
||||||
|
layout_direction,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let bounds = window_bounds
|
let bounds = window_bounds
|
||||||
|
@ -960,6 +963,7 @@ impl Window {
|
||||||
show,
|
show,
|
||||||
display_id,
|
display_id,
|
||||||
window_min_size,
|
window_min_size,
|
||||||
|
layout_direction,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
let display_id = platform_window.display().map(|display| display.id());
|
let display_id = platform_window.display().map(|display| display.id());
|
||||||
|
@ -1145,6 +1149,8 @@ impl Window {
|
||||||
root: None,
|
root: None,
|
||||||
element_id_stack: SmallVec::default(),
|
element_id_stack: SmallVec::default(),
|
||||||
text_style_stack: Vec::new(),
|
text_style_stack: Vec::new(),
|
||||||
|
root_layout_direction: layout_direction,
|
||||||
|
bidi_style_stack: Vec::new(),
|
||||||
rendered_entity_stack: Vec::new(),
|
rendered_entity_stack: Vec::new(),
|
||||||
element_offset_stack: Vec::new(),
|
element_offset_stack: Vec::new(),
|
||||||
content_mask_stack: Vec::new(),
|
content_mask_stack: Vec::new(),
|
||||||
|
@ -1371,6 +1377,18 @@ impl Window {
|
||||||
style
|
style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The current layout direction. Which is composed of all the style refinements provided to
|
||||||
|
/// `with_bidi_style` on top of the default window layout direction.
|
||||||
|
pub fn current_layout_direction(&self) -> LayoutDirection {
|
||||||
|
let mut style = BidiStyle {
|
||||||
|
dir: self.root_layout_direction,
|
||||||
|
};
|
||||||
|
for refinement in &self.bidi_style_stack {
|
||||||
|
style.refine(refinement);
|
||||||
|
}
|
||||||
|
style.dir
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if the platform window is maximized
|
/// Check if the platform window is maximized
|
||||||
/// On some platforms (namely Windows) this is different than the bounds being the size of the display
|
/// On some platforms (namely Windows) this is different than the bounds being the size of the display
|
||||||
pub fn is_maximized(&self) -> bool {
|
pub fn is_maximized(&self) -> bool {
|
||||||
|
@ -2274,6 +2292,24 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push a bidi style onto the stack, and call a function with that style active.
|
||||||
|
/// Use [`Window::bidi_style`] to get the current, combined bidi style. This method
|
||||||
|
/// should only be called as part of element drawing.
|
||||||
|
pub fn with_bidi_style<F, R>(&mut self, style: Option<BidiStyleRefinement>, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R,
|
||||||
|
{
|
||||||
|
self.invalidator.debug_assert_paint_or_prepaint();
|
||||||
|
if let Some(style) = style {
|
||||||
|
self.bidi_style_stack.push(style);
|
||||||
|
let result = f(self);
|
||||||
|
self.bidi_style_stack.pop();
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates the cursor style at the platform level. This method should only be called
|
/// Updates the cursor style at the platform level. This method should only be called
|
||||||
/// during the prepaint phase of element drawing.
|
/// during the prepaint phase of element drawing.
|
||||||
pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
|
pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
|
||||||
|
|
|
@ -26,9 +26,9 @@ use git_ui::git_panel::GitPanel;
|
||||||
use git_ui::project_diff::ProjectDiffToolbar;
|
use git_ui::project_diff::ProjectDiffToolbar;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, App, AppContext as _, Context, DismissEvent, Element, Entity, Focusable, KeyBinding,
|
Action, App, AppContext as _, Context, DismissEvent, Element, Entity, Focusable, KeyBinding,
|
||||||
ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled, Task,
|
LayoutDirection, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString,
|
||||||
TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache, point,
|
Styled, Task, TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions,
|
||||||
px, retain_all,
|
image_cache, point, px, retain_all,
|
||||||
};
|
};
|
||||||
use image_viewer::ImageInfo;
|
use image_viewer::ImageInfo;
|
||||||
use language::Capability;
|
use language::Capability;
|
||||||
|
@ -296,6 +296,7 @@ pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut App) -> WindowO
|
||||||
display_id: display.map(|display| display.id()),
|
display_id: display.map(|display| display.id()),
|
||||||
window_background: cx.theme().window_background_appearance(),
|
window_background: cx.theme().window_background_appearance(),
|
||||||
app_id: Some(app_id.to_owned()),
|
app_id: Some(app_id.to_owned()),
|
||||||
|
layout_direction: LayoutDirection::LeftToRight,
|
||||||
window_decorations: Some(window_decorations),
|
window_decorations: Some(window_decorations),
|
||||||
window_min_size: Some(gpui::Size {
|
window_min_size: Some(gpui::Size {
|
||||||
width: px(360.0),
|
width: px(360.0),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue