Skip to content

CSS-like Border #4019

@emilk

Description

@emilk

As a first step towards more powerful styling, I want to change how Frame works to be slightly more like the CSS box model.

In particular, the frame border stroke width should be counted as part of the width of the frame. This should let us get rid of the ugly clip_rect_margin.

The plan is to use the new Frame for essentially all widgets (except maybe Label). So a Button would ask the Style for a Frame, then use that for both its sizing and color calculations.

Here is the proposed outline

/// A frame around some content, including margin and border colors.
/// 
/// The total (outer) size of a frame is
/// `content_size + inner_margin + border.stroke.width + outer_margin`.
/// 
/// Everything within the border stroke is filled with the border fill color (if any).
/// 
/// ```
/// +---------------^-------------------------------+
/// |               | outer_margin                  |
/// |   +-----------v----^-----------------------+  |
/// |   |                | border stroke width   |  |
/// |   |  +-------------v---^----------------+  |  |
/// |   |  |                 | inner_margin   |  |  |
/// |   |  |  +--------------v-------------+  |  |  |
/// |   |  |  |                            |  |  |  |
/// |   |  |  |                            |  |  |  |
/// |   |  |  | content rect               |  |  |  |
/// |   |  |  +----------------------------+  |  |  |
/// |   |  |                                  |  |  |
/// |   |  +----------------------------------+  |  |
/// |   |       widget rect                      |  |
/// |   +----------------------------------------+  |
/// |           outer rect                          |
/// +-----------------------------------------------+
/// ```
/// 
/// `widget rect` is the interactive part of the widget (what sense clicks etc),
/// and is what is returned by [`Response::rect`].
struct Frame {
    /// Inner spacing. Called `padding` in CSS.
    pub inner_margin: Margin,

    /// Stroke, fill color etc.
    ///
    /// The width of the border is counted as part of the size of the frame.
    pub border: Border,

    /// Optional drop-shadow.
    /// Does not affect margins or sizes.
    pub shadow: Shadow,

    /// Outer spacing. Called `margin` in CSS.
    /// 
    /// This does NOT do "Margin Collapse" like in CSS,
    /// i.e. when placing two frames next to each other,
    /// the distance between their borders is the SUM
    /// of their other margin.
    /// In CSS the distance would be the MAX of their outer margins.
    ///
    /// Supporting margin collapse is difficult, and would 
    /// requires complicating the already complicated egui layout code.
    /// 
    /// Consider using [`egui::Spacing::item_spacing`] instead
    /// for adding space between widgets.
    pub outer_margin:  Margin,

    /// The inner rectangle must be at least this size.
    pub min_size: Vec2,
}

/// Stroke, fill, rounding, etc.
/// 
/// Controls the look of widgets and [`RectShape`].
///
/// Similar to a CSS border.
struct Border {
    /// Fills the content and inner padding of a [`Frame`].
    ///
    /// In CSS this is called `background`.
    pub fill: Color32,

    pub stroke: Stroke,

    pub rounding: Rounding,

    // TODO: add texturing here eventually
}

struct RectShape {
    /// The rectangle on which edge the border stroke is painted.
    pub rect: Rect,

    pub border: Border,

    // TODO: consider moving these into `Border`.
    // Care must be taken so that `Border` and `Frame` doesn't become too huge in common cases,
    // i.e. when there is no texture, and the rounding is uniform.
    pub fill_texture_id: TextureId,
    pub uv: Rect,
}

Metadata

Metadata

Assignees

Labels

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions