Detailed changes
@@ -491,14 +491,11 @@ pub struct Window {
sprite_atlas: Arc<dyn PlatformAtlas>,
text_system: Arc<WindowTextSystem>,
rem_size: Pixels,
- /// An override value for the window's rem size.
+ /// The stack of override values for the window's rem size.
///
/// This is used by `with_rem_size` to allow rendering an element tree with
/// a given rem size.
- ///
- /// Note: Right now we only allow for a single override value at a time, but
- /// this could likely be changed to be a stack of rem sizes.
- rem_size_override: Option<Pixels>,
+ rem_size_override_stack: SmallVec<[Pixels; 8]>,
pub(crate) viewport_size: Size<Pixels>,
layout_engine: Option<TaffyLayoutEngine>,
pub(crate) root_view: Option<AnyView>,
@@ -771,7 +768,7 @@ impl Window {
sprite_atlas,
text_system,
rem_size: px(16.),
- rem_size_override: None,
+ rem_size_override_stack: SmallVec::new(),
viewport_size: content_size,
layout_engine: Some(TaffyLayoutEngine::new()),
root_view: None,
@@ -1212,7 +1209,9 @@ impl<'a> WindowContext<'a> {
/// UI to scale, just like zooming a web page.
pub fn rem_size(&self) -> Pixels {
self.window
- .rem_size_override
+ .rem_size_override_stack
+ .last()
+ .copied()
.unwrap_or(self.window.rem_size)
}
@@ -1238,9 +1237,9 @@ impl<'a> WindowContext<'a> {
);
if let Some(rem_size) = rem_size {
- self.window.rem_size_override = Some(rem_size.into());
+ self.window.rem_size_override_stack.push(rem_size.into());
let result = f(self);
- self.window.rem_size_override.take();
+ self.window.rem_size_override_stack.pop();
result
} else {
f(self)
@@ -7,6 +7,7 @@ mod picker;
mod scroll;
mod text;
mod viewport_units;
+mod with_rem_size;
pub use auto_height_editor::*;
pub use cursor::*;
@@ -17,3 +18,4 @@ pub use picker::*;
pub use scroll::*;
pub use text::*;
pub use viewport_units::*;
+pub use with_rem_size::*;
@@ -0,0 +1,61 @@
+use gpui::{AnyElement, Hsla, Render};
+use story::Story;
+
+use ui::{prelude::*, WithRemSize};
+
+pub struct WithRemSizeStory;
+
+impl Render for WithRemSizeStory {
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+ Story::container().child(
+ Example::new(16., gpui::red())
+ .child(
+ Example::new(24., gpui::green())
+ .child(Example::new(8., gpui::blue()))
+ .child(Example::new(16., gpui::yellow())),
+ )
+ .child(
+ Example::new(12., gpui::green())
+ .child(Example::new(48., gpui::blue()))
+ .child(Example::new(16., gpui::yellow())),
+ ),
+ )
+ }
+}
+
+#[derive(IntoElement)]
+struct Example {
+ rem_size: Pixels,
+ border_color: Hsla,
+ children: Vec<AnyElement>,
+}
+
+impl Example {
+ pub fn new(rem_size: impl Into<Pixels>, border_color: Hsla) -> Self {
+ Self {
+ rem_size: rem_size.into(),
+ border_color,
+ children: Vec::new(),
+ }
+ }
+}
+
+impl ParentElement for Example {
+ fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
+ self.children.extend(elements);
+ }
+}
+
+impl RenderOnce for Example {
+ fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
+ WithRemSize::new(self.rem_size).child(
+ v_flex()
+ .gap_2()
+ .p_2()
+ .border_2()
+ .border_color(self.border_color)
+ .child(Label::new(format!("1rem = {}px", self.rem_size.0)))
+ .children(self.children),
+ )
+ }
+}
@@ -40,6 +40,7 @@ pub enum ComponentStory {
ToggleButton,
ToolStrip,
ViewportUnits,
+ WithRemSize,
}
impl ComponentStory {
@@ -76,6 +77,7 @@ impl ComponentStory {
Self::ToggleButton => cx.new_view(|_| ui::ToggleButtonStory).into(),
Self::ToolStrip => cx.new_view(|_| ui::ToolStripStory).into(),
Self::ViewportUnits => cx.new_view(|_| crate::stories::ViewportUnitsStory).into(),
+ Self::WithRemSize => cx.new_view(|_| crate::stories::WithRemSizeStory).into(),
Self::Picker => PickerStory::new(cx).into(),
}
}
@@ -13,12 +13,13 @@ mod styled_ext;
mod styles;
pub mod utils;
mod visible_on_hover;
+mod with_rem_size;
pub use clickable::*;
pub use components::*;
pub use disableable::*;
pub use fixed::*;
pub use prelude::*;
-
pub use styled_ext::*;
pub use styles::*;
+pub use with_rem_size::*;
@@ -0,0 +1,75 @@
+use gpui::{
+ div, AnyElement, Bounds, Div, DivFrameState, Element, ElementId, GlobalElementId, Hitbox,
+ IntoElement, LayoutId, ParentElement, Pixels, WindowContext,
+};
+
+/// An element that sets a particular rem size for its children.
+pub struct WithRemSize {
+ div: Div,
+ rem_size: Pixels,
+}
+
+impl WithRemSize {
+ pub fn new(rem_size: impl Into<Pixels>) -> Self {
+ Self {
+ div: div(),
+ rem_size: rem_size.into(),
+ }
+ }
+}
+
+impl ParentElement for WithRemSize {
+ fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
+ self.div.extend(elements)
+ }
+}
+
+impl Element for WithRemSize {
+ type RequestLayoutState = DivFrameState;
+ type PrepaintState = Option<Hitbox>;
+
+ fn id(&self) -> Option<ElementId> {
+ self.div.id()
+ }
+
+ fn request_layout(
+ &mut self,
+ id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
+ cx.with_rem_size(Some(self.rem_size), |cx| self.div.request_layout(id, cx))
+ }
+
+ fn prepaint(
+ &mut self,
+ id: Option<&GlobalElementId>,
+ bounds: Bounds<Pixels>,
+ request_layout: &mut Self::RequestLayoutState,
+ cx: &mut WindowContext,
+ ) -> Self::PrepaintState {
+ cx.with_rem_size(Some(self.rem_size), |cx| {
+ self.div.prepaint(id, bounds, request_layout, cx)
+ })
+ }
+
+ fn paint(
+ &mut self,
+ id: Option<&GlobalElementId>,
+ bounds: Bounds<Pixels>,
+ request_layout: &mut Self::RequestLayoutState,
+ prepaint: &mut Self::PrepaintState,
+ cx: &mut WindowContext,
+ ) {
+ cx.with_rem_size(Some(self.rem_size), |cx| {
+ self.div.paint(id, bounds, request_layout, prepaint, cx)
+ })
+ }
+}
+
+impl IntoElement for WithRemSize {
+ type Element = Self;
+
+ fn into_element(self) -> Self::Element {
+ self
+ }
+}