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::{
|
||||
App, Context, EventEmitter, IntoElement, PlatformDisplay, Size, Window,
|
||||
App, Context, EventEmitter, IntoElement, LayoutDirection, PlatformDisplay, Size, Window,
|
||||
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
|
||||
linear_color_stop, linear_gradient, point,
|
||||
};
|
||||
|
@ -61,6 +61,7 @@ impl AgentNotification {
|
|||
window_background: WindowBackgroundAppearance::Transparent,
|
||||
app_id: Some(app_id.to_owned()),
|
||||
window_min_size: None,
|
||||
layout_direction: LayoutDirection::LeftToRight,
|
||||
window_decorations: Some(WindowDecorations::Client),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::{rc::Rc, sync::Arc};
|
|||
|
||||
pub use collab_panel::CollabPanel;
|
||||
use gpui::{
|
||||
App, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
|
||||
App, LayoutDirection, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
|
||||
WindowDecorations, WindowKind, WindowOptions, point,
|
||||
};
|
||||
use panel_settings::MessageEditorSettings;
|
||||
|
@ -64,6 +64,7 @@ fn notification_window_options(
|
|||
display_id: Some(screen.id()),
|
||||
window_background: WindowBackgroundAppearance::Transparent,
|
||||
app_id: Some(app_id.to_owned()),
|
||||
layout_direction: LayoutDirection::LeftToRight,
|
||||
window_min_size: None,
|
||||
window_decorations: Some(WindowDecorations::Client),
|
||||
}
|
||||
|
|
|
@ -310,3 +310,7 @@ path = "examples/window_shadow.rs"
|
|||
[[example]]
|
||||
name = "grid_layout"
|
||||
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::{
|
||||
App, Application, Bounds, Context, DisplayId, Hsla, Pixels, SharedString, Size, Window,
|
||||
WindowBackgroundAppearance, WindowBounds, WindowKind, WindowOptions, div, point, prelude::*,
|
||||
px, rgb,
|
||||
App, Application, Bounds, Context, DisplayId, Hsla, LayoutDirection, Pixels, SharedString,
|
||||
Size, Window, WindowBackgroundAppearance, WindowBounds, WindowKind, WindowOptions, div, point,
|
||||
prelude::*, px, rgb,
|
||||
};
|
||||
|
||||
struct WindowContent {
|
||||
|
@ -60,6 +60,7 @@ fn build_window_options(display_id: DisplayId, bounds: Bounds<Pixels>) -> Window
|
|||
kind: WindowKind::PopUp,
|
||||
is_movable: false,
|
||||
app_id: None,
|
||||
layout_direction: LayoutDirection::LeftToRight,
|
||||
window_min_size: None,
|
||||
window_decorations: None,
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@ use crate::{
|
|||
Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext, Asset,
|
||||
AssetSource, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DispatchPhase, DisplayId,
|
||||
EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, KeyContext,
|
||||
Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
|
||||
PlatformDisplay, PlatformKeyboardLayout, Point, PromptBuilder, PromptButton, PromptHandle,
|
||||
PromptLevel, Render, RenderImage, RenderablePromptHandle, Reservation, ScreenCaptureSource,
|
||||
SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance,
|
||||
WindowHandle, WindowId, WindowInvalidator,
|
||||
Keymap, Keystroke, LayoutDirection, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions,
|
||||
Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, Point, PromptBuilder, PromptButton,
|
||||
PromptHandle, PromptLevel, Render, RenderImage, RenderablePromptHandle, Reservation,
|
||||
ScreenCaptureSource, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window,
|
||||
WindowAppearance, WindowHandle, WindowId, WindowInvalidator,
|
||||
colors::{Colors, GlobalColors},
|
||||
current_platform, hash, init_app_menus,
|
||||
};
|
||||
|
@ -1539,6 +1539,17 @@ impl App {
|
|||
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
|
||||
pub fn set_dock_menu(&self, menus: Vec<MenuItem>) {
|
||||
self.platform.set_dock_menu(menus, &self.keymap.borrow())
|
||||
|
|
|
@ -1294,13 +1294,15 @@ impl Element for Div {
|
|||
window,
|
||||
cx,
|
||||
|style, window, cx| {
|
||||
window.with_text_style(style.text_style().cloned(), |window| {
|
||||
child_layout_ids = self
|
||||
.children
|
||||
.iter_mut()
|
||||
.map(|child| child.request_layout(window, cx))
|
||||
.collect::<SmallVec<_>>();
|
||||
window.request_layout(style, child_layout_ids.iter().copied(), cx)
|
||||
window.with_bidi_style(style.bidi_style().cloned(), |window| {
|
||||
window.with_text_style(style.text_style().cloned(), |window| {
|
||||
child_layout_ids = self
|
||||
.children
|
||||
.iter_mut()
|
||||
.map(|child| child.request_layout(window, 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_content_mask(
|
||||
style.overflow_mask(bounds, window.rem_size()),
|
||||
|window| {
|
||||
let hitbox = if self.should_insert_hitbox(&style, window, cx) {
|
||||
Some(window.insert_hitbox(bounds, self.hitbox_behavior))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
window.with_bidi_style(style.bidi_style().cloned(), |window| {
|
||||
window.with_text_style(style.text_style().cloned(), |window| {
|
||||
window.with_content_mask(
|
||||
style.overflow_mask(bounds, window.rem_size()),
|
||||
|window| {
|
||||
let hitbox = if self.should_insert_hitbox(&style, window, cx) {
|
||||
Some(window.insert_hitbox(bounds, self.hitbox_behavior))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let scroll_offset =
|
||||
self.clamp_scroll_position(bounds, &style, window, cx);
|
||||
let result = f(&style, scroll_offset, hitbox, window, cx);
|
||||
(result, element_state)
|
||||
},
|
||||
)
|
||||
let scroll_offset =
|
||||
self.clamp_scroll_position(bounds, &style, window, cx);
|
||||
let result = f(&style, scroll_offset, hitbox, window, cx);
|
||||
(result, element_state)
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
|
@ -1763,61 +1767,65 @@ impl Interactivity {
|
|||
|
||||
window.with_element_opacity(style.opacity, |window| {
|
||||
style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
|
||||
window.with_text_style(style.text_style().cloned(), |window| {
|
||||
window.with_content_mask(
|
||||
style.overflow_mask(bounds, window.rem_size()),
|
||||
|window| {
|
||||
if let Some(hitbox) = hitbox {
|
||||
#[cfg(debug_assertions)]
|
||||
self.paint_debug_info(
|
||||
global_id, hitbox, &style, window, cx,
|
||||
);
|
||||
window.with_bidi_style(style.bidi_style().cloned(), |window| {
|
||||
window.with_text_style(style.text_style().cloned(), |window| {
|
||||
window.with_content_mask(
|
||||
style.overflow_mask(bounds, window.rem_size()),
|
||||
|window| {
|
||||
if let Some(hitbox) = hitbox {
|
||||
#[cfg(debug_assertions)]
|
||||
self.paint_debug_info(
|
||||
global_id, hitbox, &style, window, cx,
|
||||
);
|
||||
|
||||
if let Some(drag) = cx.active_drag.as_ref() {
|
||||
if let Some(mouse_cursor) = drag.cursor_style {
|
||||
window.set_window_cursor_style(mouse_cursor);
|
||||
if let Some(drag) = cx.active_drag.as_ref() {
|
||||
if let Some(mouse_cursor) = drag.cursor_style {
|
||||
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 {
|
||||
window.set_cursor_style(mouse_cursor, hitbox);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
Some(crate::TextStyleRefinement {
|
||||
color: Some(crate::red()),
|
||||
line_height: Some(FONT_SIZE.into()),
|
||||
background_color: Some(crate::white()),
|
||||
..Default::default()
|
||||
}),
|
||||
render_debug_text,
|
||||
)
|
||||
window.with_bidi_style(None, |window| {
|
||||
window.with_text_style(
|
||||
Some(crate::TextStyleRefinement {
|
||||
color: Some(crate::red()),
|
||||
line_height: Some(FONT_SIZE.into()),
|
||||
background_color: Some(crate::white()),
|
||||
..Default::default()
|
||||
}),
|
||||
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 mut line_origin = bounds.origin;
|
||||
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 {
|
||||
line.paint_background(
|
||||
line_origin,
|
||||
line_height,
|
||||
text_style.text_align,
|
||||
text_align,
|
||||
Some(bounds),
|
||||
window,
|
||||
cx,
|
||||
|
@ -455,7 +458,7 @@ impl TextLayout {
|
|||
line.paint(
|
||||
line_origin,
|
||||
line_height,
|
||||
text_style.text_align,
|
||||
text_align,
|
||||
Some(bounds),
|
||||
window,
|
||||
cx,
|
||||
|
|
|
@ -38,10 +38,10 @@ pub(crate) mod scap_screen_capture;
|
|||
use crate::{
|
||||
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
|
||||
DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
|
||||
ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput,
|
||||
Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
|
||||
ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window,
|
||||
WindowControlArea, hash, point, px, size,
|
||||
ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LayoutDirection, LineLayout,
|
||||
Pixels, PlatformInput, Point, RenderGlyphParams, RenderImage, RenderImageParams,
|
||||
RenderSvgParams, ScaledPixels, Scene, ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer,
|
||||
SvgSize, Task, TaskLabel, Window, WindowControlArea, hash, point, px, size,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_task::Runnable;
|
||||
|
@ -238,6 +238,9 @@ pub(crate) trait Platform: 'static {
|
|||
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 perform_dock_menu_action(&self, _action: usize) {}
|
||||
fn add_recent_document(&self, _path: &Path) {}
|
||||
|
@ -1105,6 +1108,9 @@ pub struct WindowOptions {
|
|||
/// Whether to use client or server side decorations. Wayland only
|
||||
/// Note that this may be ignored.
|
||||
pub window_decorations: Option<WindowDecorations>,
|
||||
|
||||
/// The layout direction
|
||||
pub layout_direction: LayoutDirection,
|
||||
}
|
||||
|
||||
/// 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 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.
|
||||
|
@ -1194,6 +1203,7 @@ impl Default for WindowOptions {
|
|||
app_id: None,
|
||||
window_min_size: None,
|
||||
window_decorations: None,
|
||||
layout_direction: LayoutDirection::LeftToRight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ use xkbcommon::xkb::{self, Keycode, Keysym, State};
|
|||
|
||||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
||||
ForegroundExecutor, Keymap, LinuxDispatcher, Menu, MenuItem, OwnedMenu, PathPromptOptions,
|
||||
Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow,
|
||||
Point, Result, Task, WindowAppearance, WindowParams, px,
|
||||
ForegroundExecutor, Keymap, LayoutDirection, LinuxDispatcher, Menu, MenuItem, OwnedMenu,
|
||||
PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout,
|
||||
PlatformTextSystem, PlatformWindow, Point, Result, Task, WindowAppearance, WindowParams, px,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "wayland", feature = "x11"))]
|
||||
|
@ -95,6 +95,7 @@ pub(crate) struct LinuxCommon {
|
|||
pub(crate) callbacks: PlatformHandlers,
|
||||
pub(crate) signal: LoopSignal,
|
||||
pub(crate) menus: Vec<OwnedMenu>,
|
||||
pub(crate) default_layout_direction: LayoutDirection,
|
||||
}
|
||||
|
||||
impl LinuxCommon {
|
||||
|
@ -120,6 +121,7 @@ impl LinuxCommon {
|
|||
auto_hide_scrollbars: false,
|
||||
callbacks,
|
||||
signal,
|
||||
default_layout_direction: LayoutDirection::RightToLeft,
|
||||
menus: Vec::new(),
|
||||
};
|
||||
|
||||
|
@ -563,6 +565,16 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||
}
|
||||
|
||||
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"))]
|
||||
|
|
|
@ -6,9 +6,9 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardEntry, ClipboardItem, ClipboardString,
|
||||
CursorStyle, ForegroundExecutor, Image, ImageFormat, KeyContext, Keymap, MacDispatcher,
|
||||
MacDisplay, MacWindow, Menu, MenuItem, OsMenu, OwnedMenu, PathPromptOptions, Platform,
|
||||
PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow, Result,
|
||||
CursorStyle, ForegroundExecutor, Image, ImageFormat, KeyContext, Keymap, LayoutDirection,
|
||||
MacDispatcher, MacDisplay, MacWindow, Menu, MenuItem, OsMenu, OwnedMenu, PathPromptOptions,
|
||||
Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow, Result,
|
||||
SemanticVersion, SystemMenuType, Task, WindowAppearance, WindowParams, hash,
|
||||
};
|
||||
use anyhow::{Context as _, anyhow};
|
||||
|
@ -171,6 +171,7 @@ pub(crate) struct MacPlatformState {
|
|||
finish_launching: Option<Box<dyn FnOnce()>>,
|
||||
dock_menu: Option<id>,
|
||||
menus: Option<Vec<OwnedMenu>>,
|
||||
default_layout_direction: LayoutDirection,
|
||||
}
|
||||
|
||||
impl Default for MacPlatform {
|
||||
|
@ -209,6 +210,7 @@ impl MacPlatform {
|
|||
dock_menu: None,
|
||||
on_keyboard_layout_change: None,
|
||||
menus: None,
|
||||
default_layout_direction: LayoutDirection::LeftToRight,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -232,16 +234,21 @@ impl MacPlatform {
|
|||
delegate: id,
|
||||
actions: &mut Vec<Box<dyn Action>>,
|
||||
keymap: &Keymap,
|
||||
layout_direction: LayoutDirection,
|
||||
) -> id {
|
||||
let layout_direction = Self::ns_user_interface_layout_direction(layout_direction);
|
||||
unsafe {
|
||||
let application_menu = NSMenu::new(nil).autorelease();
|
||||
application_menu.setDelegate_(delegate);
|
||||
let _: () =
|
||||
msg_send![application_menu, setUserInterfaceLayoutDirection: layout_direction];
|
||||
|
||||
for menu_config in menus {
|
||||
let menu = NSMenu::new(nil).autorelease();
|
||||
let menu_title = ns_string(&menu_config.name);
|
||||
menu.setTitle_(menu_title);
|
||||
menu.setDelegate_(delegate);
|
||||
let _: () = msg_send![menu, setUserInterfaceLayoutDirection: layout_direction];
|
||||
|
||||
for item_config in &menu_config.items {
|
||||
menu.addItem_(Self::create_menu_item(
|
||||
|
@ -446,6 +453,13 @@ impl MacPlatform {
|
|||
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 {
|
||||
|
@ -894,8 +908,15 @@ impl Platform for MacPlatform {
|
|||
unsafe {
|
||||
let app: id = msg_send![APP_CLASS, sharedApplication];
|
||||
let mut state = self.0.lock();
|
||||
let layout_direction = state.default_layout_direction;
|
||||
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);
|
||||
app.setMainMenu_(menu);
|
||||
}
|
||||
|
@ -1219,6 +1240,16 @@ impl Platform for MacPlatform {
|
|||
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 {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};
|
||||
use crate::{
|
||||
AnyWindowHandle, Bounds, Capslock, DisplayLink, ExternalPaths, FileDropEvent,
|
||||
ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
|
||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
|
||||
PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions,
|
||||
ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
|
||||
WindowControlArea, WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size,
|
||||
ForegroundExecutor, KeyDownEvent, Keystroke, LayoutDirection, Modifiers, ModifiersChangedEvent,
|
||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
|
||||
PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel,
|
||||
RequestFrameOptions, ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance,
|
||||
WindowBounds, WindowControlArea, WindowKind, WindowParams, platform::PlatformInputHandler,
|
||||
point, px, size,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
|
@ -367,6 +368,7 @@ struct MacWindowState {
|
|||
last_key_equivalent: Option<KeyDownEvent>,
|
||||
synthetic_drag_counter: usize,
|
||||
traffic_light_position: Option<Point<Pixels>>,
|
||||
layout_direction: LayoutDirection,
|
||||
transparent_titlebar: bool,
|
||||
previous_modifiers_changed_event: Option<PlatformInput>,
|
||||
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 zoom_button_frame: CGRect = msg_send![zoom_button, frame];
|
||||
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
|
||||
- traffic_light_position.y
|
||||
- px(close_button_frame.size.height as f32),
|
||||
);
|
||||
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());
|
||||
let _: () = msg_send![close_button, setFrame: close_button_frame];
|
||||
|
@ -534,6 +547,7 @@ impl MacWindow {
|
|||
show,
|
||||
display_id,
|
||||
window_min_size,
|
||||
layout_direction,
|
||||
}: WindowParams,
|
||||
executor: ForegroundExecutor,
|
||||
renderer_context: renderer::Context,
|
||||
|
@ -660,6 +674,7 @@ impl MacWindow {
|
|||
external_files_dragged: false,
|
||||
first_mouse: false,
|
||||
fullscreen_restore_bounds: Bounds::default(),
|
||||
layout_direction: layout_direction,
|
||||
})));
|
||||
|
||||
(*native_window).set_ivar(
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::{
|
||||
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
|
||||
ForegroundExecutor, Keymap, NoopTextSystem, Platform, PlatformDisplay, PlatformKeyboardLayout,
|
||||
PlatformTextSystem, PromptButton, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream,
|
||||
SourceMetadata, Task, TestDisplay, TestWindow, WindowAppearance, WindowParams, size,
|
||||
ForegroundExecutor, Keymap, LayoutDirection, NoopTextSystem, Platform, PlatformDisplay,
|
||||
PlatformKeyboardLayout, PlatformTextSystem, PromptButton, ScreenCaptureFrame,
|
||||
ScreenCaptureSource, ScreenCaptureStream, SourceMetadata, Task, TestDisplay, TestWindow,
|
||||
WindowAppearance, WindowParams, size,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::VecDeque;
|
||||
|
@ -38,6 +39,7 @@ pub(crate) struct TestPlatform {
|
|||
#[cfg(target_os = "windows")]
|
||||
bitmap_factory: std::mem::ManuallyDrop<IWICImagingFactory>,
|
||||
weak: Weak<Self>,
|
||||
default_layout_direction: RefCell<LayoutDirection>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -119,6 +121,7 @@ impl TestPlatform {
|
|||
#[cfg(target_os = "windows")]
|
||||
bitmap_factory,
|
||||
text_system,
|
||||
default_layout_direction: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -426,6 +429,14 @@ impl Platform for TestPlatform {
|
|||
fn open_with_system(&self, _path: &Path) {
|
||||
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 {
|
||||
|
|
|
@ -54,6 +54,7 @@ pub(crate) struct WindowsPlatformState {
|
|||
jump_list: JumpList,
|
||||
// NOTE: standard cursor handles don't need to close.
|
||||
pub(crate) current_cursor: Option<HCURSOR>,
|
||||
default_layout_direction: LayoutDirection,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -77,6 +78,7 @@ impl WindowsPlatformState {
|
|||
callbacks,
|
||||
jump_list,
|
||||
current_cursor,
|
||||
default_layout_direction: LayoutDirection::RightToLeft,
|
||||
menus: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -707,6 +709,14 @@ impl Platform for WindowsPlatform {
|
|||
) -> Vec<SmallVec<[PathBuf; 2]>> {
|
||||
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 {
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#![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::{
|
||||
cell::RefCell,
|
||||
num::NonZeroIsize,
|
||||
|
@ -9,13 +17,7 @@ use std::{
|
|||
sync::{Arc, Once},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
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::Win32::Graphics::Dwm::{DWMWA_NONCLIENT_RTL_LAYOUT, DwmSetWindowAttribute};
|
||||
use windows::{
|
||||
Win32::{
|
||||
Foundation::*,
|
||||
|
@ -26,8 +28,6 @@ use windows::{
|
|||
core::*,
|
||||
};
|
||||
|
||||
use crate::*;
|
||||
|
||||
pub(crate) struct WindowsWindow(pub Rc<WindowsWindowInner>);
|
||||
|
||||
pub struct WindowsWindowState {
|
||||
|
@ -167,7 +167,7 @@ impl WindowsWindowState {
|
|||
fn calculate_window_bounds(&self) -> (Bounds<Pixels>, bool) {
|
||||
let placement = unsafe {
|
||||
let mut placement = WINDOWPLACEMENT {
|
||||
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||
length: size_of::<WINDOWPLACEMENT>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
GetWindowPlacement(self.hwnd, &mut placement).log_err();
|
||||
|
@ -438,6 +438,18 @@ impl WindowsWindow {
|
|||
let this = context.inner.take().unwrap()?;
|
||||
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)?;
|
||||
configure_dwm_dark_mode(hwnd, appearance);
|
||||
this.state.borrow_mut().border_offset.update(hwnd)?;
|
||||
|
@ -600,7 +612,7 @@ impl PlatformWindow for WindowsWindow {
|
|||
.spawn(async move {
|
||||
unsafe {
|
||||
let mut config = TASKDIALOGCONFIG::default();
|
||||
config.cbSize = std::mem::size_of::<TASKDIALOGCONFIG>() as _;
|
||||
config.cbSize = size_of::<TASKDIALOGCONFIG>() as _;
|
||||
config.hwndParent = handle;
|
||||
let title;
|
||||
let main_icon;
|
||||
|
@ -1271,7 +1283,7 @@ fn retrieve_window_placement(
|
|||
border_offset: WindowBorderOffset,
|
||||
) -> Result<WINDOWPLACEMENT> {
|
||||
let mut placement = WINDOWPLACEMENT {
|
||||
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||
length: size_of::<WINDOWPLACEMENT>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
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 {
|
||||
attrib: 0x13,
|
||||
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 _);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
fmt::Debug,
|
||||
hash::{Hash, Hasher},
|
||||
iter, mem,
|
||||
ops::Range,
|
||||
|
@ -254,6 +255,9 @@ pub struct Style {
|
|||
/// The text style of this element
|
||||
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.
|
||||
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
|
||||
/// for a single font, uniformly sized and spaced text.
|
||||
#[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.
|
||||
/// If the element does not hide its overflow, this will return `None`.
|
||||
pub fn overflow_mask(
|
||||
|
@ -773,6 +866,7 @@ impl Default for Style {
|
|||
corner_radii: Corners::default(),
|
||||
box_shadow: Default::default(),
|
||||
text: TextStyleRefinement::default(),
|
||||
bidi: BidiStyleRefinement::default(),
|
||||
mouse_cursor: None,
|
||||
opacity: None,
|
||||
grid_rows: None,
|
||||
|
@ -1166,6 +1260,18 @@ pub enum FlexDirection {
|
|||
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
|
||||
///
|
||||
/// 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,
|
||||
TextAlign, TextOverflow, TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems,
|
||||
};
|
||||
use crate::{BidiStyleRefinement, LayoutDirection};
|
||||
pub use gpui_macros::{
|
||||
border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods,
|
||||
overflow_style_methods, padding_style_methods, position_style_methods,
|
||||
|
@ -743,6 +744,22 @@ pub trait Styled: Sized {
|
|||
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.
|
||||
#[cfg(debug_assertions)]
|
||||
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()),
|
||||
justify_content: self.justify_content.map(|x| x.into()),
|
||||
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_basis: self.flex_basis.to_taffy(rem_size),
|
||||
flex_grow: self.flex_grow,
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
use crate::Inspector;
|
||||
use crate::{
|
||||
Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
|
||||
AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock,
|
||||
Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
|
||||
DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
|
||||
FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero,
|
||||
KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
|
||||
LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent,
|
||||
MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
||||
PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad,
|
||||
Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
|
||||
SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size,
|
||||
StrikethroughStyle, Style, SubscriberSet, Subscription, TabHandles, TaffyLayoutEngine, Task,
|
||||
TextStyle, TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle,
|
||||
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations,
|
||||
WindowOptions, WindowParams, WindowTextSystem, point, prelude::*, px, rems, size,
|
||||
transparent_black,
|
||||
AsyncWindowContext, AvailableSpace, Background, BidiStyle, BidiStyleRefinement, BorderStyle,
|
||||
Bounds, BoxShadow, Capslock, Context, Corners, CursorStyle, Decorations, DevicePixels,
|
||||
DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
|
||||
EntityId, EventEmitter, FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs,
|
||||
Hsla, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke,
|
||||
KeystrokeEvent, LayoutDirection, LayoutId, LineLayoutIndex, Modifiers, ModifiersChangedEvent,
|
||||
MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||
PolychromeSprite, PromptButton, PromptLevel, Quad, Render, RenderGlyphParams, RenderImage,
|
||||
RenderImageParams, RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR,
|
||||
SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
|
||||
SubscriberSet, Subscription, TabHandles, TaffyLayoutEngine, Task, TextStyle,
|
||||
TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance,
|
||||
WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowOptions,
|
||||
WindowParams, WindowTextSystem, point, prelude::*, px, rems, size, transparent_black,
|
||||
};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
|
@ -835,6 +835,8 @@ pub struct Window {
|
|||
pub(crate) root: Option<AnyView>,
|
||||
pub(crate) element_id_stack: SmallVec<[ElementId; 32]>,
|
||||
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) element_offset_stack: Vec<Point<Pixels>>,
|
||||
pub(crate) element_opacity: Option<f32>,
|
||||
|
@ -944,6 +946,7 @@ impl Window {
|
|||
app_id,
|
||||
window_min_size,
|
||||
window_decorations,
|
||||
layout_direction,
|
||||
} = options;
|
||||
|
||||
let bounds = window_bounds
|
||||
|
@ -960,6 +963,7 @@ impl Window {
|
|||
show,
|
||||
display_id,
|
||||
window_min_size,
|
||||
layout_direction,
|
||||
},
|
||||
)?;
|
||||
let display_id = platform_window.display().map(|display| display.id());
|
||||
|
@ -1145,6 +1149,8 @@ impl Window {
|
|||
root: None,
|
||||
element_id_stack: SmallVec::default(),
|
||||
text_style_stack: Vec::new(),
|
||||
root_layout_direction: layout_direction,
|
||||
bidi_style_stack: Vec::new(),
|
||||
rendered_entity_stack: Vec::new(),
|
||||
element_offset_stack: Vec::new(),
|
||||
content_mask_stack: Vec::new(),
|
||||
|
@ -1371,6 +1377,18 @@ impl Window {
|
|||
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
|
||||
/// On some platforms (namely Windows) this is different than the bounds being the size of the display
|
||||
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
|
||||
/// during the prepaint phase of element drawing.
|
||||
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 gpui::{
|
||||
Action, App, AppContext as _, Context, DismissEvent, Element, Entity, Focusable, KeyBinding,
|
||||
ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled, Task,
|
||||
TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache, point,
|
||||
px, retain_all,
|
||||
LayoutDirection, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString,
|
||||
Styled, Task, TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions,
|
||||
image_cache, point, px, retain_all,
|
||||
};
|
||||
use image_viewer::ImageInfo;
|
||||
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()),
|
||||
window_background: cx.theme().window_background_appearance(),
|
||||
app_id: Some(app_id.to_owned()),
|
||||
layout_direction: LayoutDirection::LeftToRight,
|
||||
window_decorations: Some(window_decorations),
|
||||
window_min_size: Some(gpui::Size {
|
||||
width: px(360.0),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue