demo: layout fixes, sticky removal, style overrides, css-like rendering improvements
This commit is contained in:
@@ -4,8 +4,7 @@
|
|||||||
// Showcases: @global, @singleton, !rhei:, @component, @if/@else, @each,
|
// Showcases: @global, @singleton, !rhei:, @component, @if/@else, @each,
|
||||||
// @on click, variable binding, positioning (fixed, absolute),
|
// @on click, variable binding, positioning (fixed, absolute),
|
||||||
// typography, box model, opacity, overflow, grid layout, style inheritance.
|
// typography, box model, opacity, overflow, grid layout, style inheritance.
|
||||||
// Note: true CSS position:sticky requires scroll-tracking not exposed by Iced.
|
// Note: position:sticky approximated via in-flow layout + overlay
|
||||||
// "Always-visible header above scrollable content" pattern used instead.
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
@version 1
|
@version 1
|
||||||
@@ -66,7 +65,7 @@
|
|||||||
// ── 3. Layout ────────────────────────────────────────────────────────────────
|
// ── 3. Layout ────────────────────────────────────────────────────────────────
|
||||||
Window(title=$title) {
|
Window(title=$title) {
|
||||||
|
|
||||||
// ── Header (always visible; body scrolls beneath — closest to sticky in Iced) ─
|
// ── Header (sticky — stays at top while body scrolls beneath) ─
|
||||||
Panel(class="header") {
|
Panel(class="header") {
|
||||||
Panel(class="header-inner") {
|
Panel(class="header-inner") {
|
||||||
Icon "◇"
|
Icon "◇"
|
||||||
@@ -76,8 +75,7 @@ Window(title=$title) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Scrollable body ─────────────────────────────────────────────────────
|
// ── Main body ────────────────────────────────────────────────────────────
|
||||||
Panel(class="body-scroll") {
|
|
||||||
Panel(class="body") {
|
Panel(class="body") {
|
||||||
|
|
||||||
// ── LEFT COLUMN ──────────────────────────────────────────────────────
|
// ── LEFT COLUMN ──────────────────────────────────────────────────────
|
||||||
@@ -199,13 +197,13 @@ Window(title=$title) {
|
|||||||
Text(class="gauge-label") "* Opacity depends on Iced widget support — works on containers, not on text"
|
Text(class="gauge-label") "* Opacity depends on Iced widget support — works on containers, not on text"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panel: Scrollable Log with always-visible header
|
// Panel: Scrollable Log with sticky header
|
||||||
Panel(class="card") {
|
Panel(class="card") {
|
||||||
Header "Scrollable Log"
|
Header "Scrollable Log"
|
||||||
Panel(class="log-header") {
|
|
||||||
Text "Event Log (always visible — above scrollable area)"
|
|
||||||
}
|
|
||||||
Panel(class="log") {
|
Panel(class="log") {
|
||||||
|
Panel(class="log-header") {
|
||||||
|
Text "Event Log (sticky header — scroll below)"
|
||||||
|
}
|
||||||
Text "Init: system boot"
|
Text "Init: system boot"
|
||||||
Text "Info: all modules OK"
|
Text "Info: all modules OK"
|
||||||
Text "Warn: CPU at $cpu%"
|
Text "Warn: CPU at $cpu%"
|
||||||
@@ -253,7 +251,6 @@ Window(title=$title) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ── Fixed Toast ──────────────────────────────────────────────────────────
|
// ── Fixed Toast ──────────────────────────────────────────────────────────
|
||||||
@if $notif {
|
@if $notif {
|
||||||
|
|||||||
@@ -45,8 +45,10 @@ Panel {
|
|||||||
direction: vertical
|
direction: vertical
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Header ───────────────────────────────────────────────────────────────────
|
// ── Header (sticky — sticks at top of Window when scrolled) ──────────────────
|
||||||
.header {
|
.header {
|
||||||
|
position: sticky
|
||||||
|
top: 0
|
||||||
background: $card
|
background: $card
|
||||||
padding: 8px 20px
|
padding: 8px 20px
|
||||||
border-bottom: 1px solid $border
|
border-bottom: 1px solid $border
|
||||||
@@ -60,13 +62,6 @@ Panel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Layout ───────────────────────────────────────────────────────────────────
|
// ── Layout ───────────────────────────────────────────────────────────────────
|
||||||
.body-scroll {
|
|
||||||
overflow-y: auto
|
|
||||||
flex-grow: 1
|
|
||||||
padding: 0px
|
|
||||||
gap: 0px
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
direction: horizontal
|
direction: horizontal
|
||||||
padding: 12px
|
padding: 12px
|
||||||
@@ -370,6 +365,8 @@ Slider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.log-header {
|
.log-header {
|
||||||
|
position: sticky
|
||||||
|
top: 0
|
||||||
background: $surface
|
background: $surface
|
||||||
padding: 6px 10px
|
padding: 6px 10px
|
||||||
border-radius: 4px
|
border-radius: 4px
|
||||||
@@ -379,8 +376,7 @@ Slider {
|
|||||||
width: fill
|
width: fill
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: position:sticky is not supported by Iced. "Always visible header above
|
// Note: position:sticky approximated via in-flow spacer + overlay (no scroll-tracking)
|
||||||
// scrollable content" is the working pattern.
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// ── Style Inheritance ────────────────────────────────────────────────────────
|
// ── Style Inheritance ────────────────────────────────────────────────────────
|
||||||
|
|||||||
17
examples/dock/dock.gltm
Normal file
17
examples/dock/dock.gltm
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@version 1
|
||||||
|
@style "styles.glts"
|
||||||
|
|
||||||
|
Window(title="Dock") {
|
||||||
|
Panel(class="wallpaper")
|
||||||
|
|
||||||
|
Panel(class="dock-outer") {
|
||||||
|
Panel(class="dock") {
|
||||||
|
Button(label=" Finder ", class="dock-icon")
|
||||||
|
Button(label=" Safari ", class="dock-icon")
|
||||||
|
Button(label=" Mail ", class="dock-icon")
|
||||||
|
Button(label=" Music ", class="dock-icon")
|
||||||
|
Button(label=" Photos ", class="dock-icon")
|
||||||
|
Button(label=" Trash ", class="dock-icon")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
examples/dock/styles.glts
Normal file
56
examples/dock/styles.glts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
$dock-bg = #2c2c2e
|
||||||
|
$icon-bg = #3a3a3c
|
||||||
|
$text = #ffffff
|
||||||
|
$accent = #0a84ff
|
||||||
|
|
||||||
|
Window {
|
||||||
|
background: #1c1c1e
|
||||||
|
height: 100vh
|
||||||
|
}
|
||||||
|
|
||||||
|
Panel {
|
||||||
|
background: transparent
|
||||||
|
border-width: 0px
|
||||||
|
padding: 0px
|
||||||
|
gap: 0px
|
||||||
|
direction: vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallpaper {
|
||||||
|
width: fill
|
||||||
|
height: fill
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock-outer {
|
||||||
|
position: fixed
|
||||||
|
bottom: 12px
|
||||||
|
width: 50%
|
||||||
|
direction: horizontal
|
||||||
|
content-align: center
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock {
|
||||||
|
direction: horizontal
|
||||||
|
gap: 6px
|
||||||
|
background: $dock-bg
|
||||||
|
padding: 8px 14px
|
||||||
|
border-radius: 16px
|
||||||
|
border-width: 1px
|
||||||
|
border-color: #444
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock-icon {
|
||||||
|
background: $icon-bg
|
||||||
|
color: $text
|
||||||
|
font-size: 13px
|
||||||
|
font-weight: 600
|
||||||
|
padding: 6px 10px
|
||||||
|
border-width: 0px
|
||||||
|
border-radius: 8px
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock-icon:hover {
|
||||||
|
background: $accent
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
24
src/app.rs
24
src/app.rs
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use iced::widget::{column, container};
|
use iced::widget::{column, container};
|
||||||
use iced::{Length, Theme};
|
use iced::{Length, Theme};
|
||||||
|
|
||||||
@@ -23,6 +25,11 @@ impl GlintApp {
|
|||||||
self.doc.variables.insert(var.clone(), Value::Float(y as f64));
|
self.doc.variables.insert(var.clone(), Value::Float(y as f64));
|
||||||
self.doc.tracker.on_variable_changed(&var);
|
self.doc.tracker.on_variable_changed(&var);
|
||||||
}
|
}
|
||||||
|
Message::ScrollableScrolled(id, y) => {
|
||||||
|
let var = format!("__scroll_{}", id);
|
||||||
|
self.doc.variables.insert(var.clone(), Value::Float(y as f64));
|
||||||
|
self.doc.tracker.on_variable_changed(&var);
|
||||||
|
}
|
||||||
Message::EventTriggered(script) => {
|
Message::EventTriggered(script) => {
|
||||||
if !script.is_empty() {
|
if !script.is_empty() {
|
||||||
let old_vars: Vec<String> = self.doc.variables.keys().cloned().collect();
|
let old_vars: Vec<String> = self.doc.variables.keys().cloned().collect();
|
||||||
@@ -74,18 +81,23 @@ impl GlintApp {
|
|||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill);
|
.height(Length::Fill);
|
||||||
|
|
||||||
|
let mut scroll_positions: HashMap<u64, f32> = HashMap::new();
|
||||||
|
for (key, val) in &self.doc.variables {
|
||||||
|
if let Some(id_str) = key.strip_prefix("__scroll_") {
|
||||||
|
if let (Ok(id), Value::Float(y)) = (id_str.parse::<u64>(), val) {
|
||||||
|
scroll_positions.insert(id, *y as f32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut global_fixed_layers = Vec::new();
|
let mut global_fixed_layers = Vec::new();
|
||||||
let mut global_abs_layers = Vec::new();
|
let mut global_abs_layers = Vec::new();
|
||||||
let mut global_sticky_layers = Vec::new();
|
let mut global_sticky_layers = Vec::new();
|
||||||
|
|
||||||
let _scroll_y = self.doc.variables.get("__scroll_y")
|
|
||||||
.and_then(|v| if let Value::Float(f) = v { Some(*f as f32) } else { None })
|
|
||||||
.unwrap_or(0.0);
|
|
||||||
|
|
||||||
for root in &self.vdom_roots {
|
for root in &self.vdom_roots {
|
||||||
if let Some(el) = render_element(root, None, None, None, &mut global_fixed_layers, &mut global_abs_layers, &mut global_sticky_layers, &self.doc.stylesheet) {
|
if let Some(el) = render_element(root, None, None, None, &mut global_fixed_layers, &mut global_abs_layers, &mut global_sticky_layers, &scroll_positions, 0, &self.doc.stylesheet) {
|
||||||
content = content.push(el);
|
content = content.push(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout = column![
|
let layout = column![
|
||||||
|
|||||||
@@ -815,35 +815,50 @@ pub enum Display {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum SizeValue {
|
||||||
|
Px(f32),
|
||||||
|
Percent(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeValue {
|
||||||
|
pub fn resolve(self, relative_to: Option<f32>) -> f32 {
|
||||||
|
match self {
|
||||||
|
SizeValue::Px(v) => v,
|
||||||
|
SizeValue::Percent(p) => relative_to.map(|base| base * p / 100.0).unwrap_or(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct ComputedStyle {
|
pub struct ComputedStyle {
|
||||||
pub font_size: Option<f32>,
|
pub font_size: Option<SizeValue>,
|
||||||
pub color: Option<iced::Color>,
|
pub color: Option<iced::Color>,
|
||||||
pub padding: Option<f32>,
|
pub padding: Option<SizeValue>,
|
||||||
pub padding_top: Option<f32>,
|
pub padding_top: Option<SizeValue>,
|
||||||
pub padding_right: Option<f32>,
|
pub padding_right: Option<SizeValue>,
|
||||||
pub padding_bottom:Option<f32>,
|
pub padding_bottom:Option<SizeValue>,
|
||||||
pub padding_left: Option<f32>,
|
pub padding_left: Option<SizeValue>,
|
||||||
|
|
||||||
pub margin: Option<f32>,
|
pub margin: Option<SizeValue>,
|
||||||
pub margin_top: Option<f32>,
|
pub margin_top: Option<SizeValue>,
|
||||||
pub margin_right: Option<f32>,
|
pub margin_right: Option<SizeValue>,
|
||||||
pub margin_bottom: Option<f32>,
|
pub margin_bottom: Option<SizeValue>,
|
||||||
pub margin_left: Option<f32>,
|
pub margin_left: Option<SizeValue>,
|
||||||
|
|
||||||
pub background: Option<iced::Color>,
|
pub background: Option<iced::Color>,
|
||||||
pub spacing: Option<f32>,
|
pub spacing: Option<SizeValue>,
|
||||||
pub border_radius: Option<f32>,
|
pub border_radius: Option<SizeValue>,
|
||||||
pub border_width: Option<f32>,
|
pub border_width: Option<SizeValue>,
|
||||||
pub border_color: Option<iced::Color>,
|
pub border_color: Option<iced::Color>,
|
||||||
|
|
||||||
pub width: Option<iced::Length>,
|
pub width: Option<iced::Length>,
|
||||||
pub height: Option<iced::Length>,
|
pub height: Option<iced::Length>,
|
||||||
|
|
||||||
pub min_width: Option<f32>,
|
pub min_width: Option<SizeValue>,
|
||||||
pub max_width: Option<f32>,
|
pub max_width: Option<SizeValue>,
|
||||||
pub min_height: Option<f32>,
|
pub min_height: Option<SizeValue>,
|
||||||
pub max_height: Option<f32>,
|
pub max_height: Option<SizeValue>,
|
||||||
pub direction: Option<LayoutDirection>,
|
pub direction: Option<LayoutDirection>,
|
||||||
pub align_items: Option<iced::Alignment>,
|
pub align_items: Option<iced::Alignment>,
|
||||||
pub content_align: Option<ContentAlign>,
|
pub content_align: Option<ContentAlign>,
|
||||||
@@ -851,10 +866,10 @@ pub struct ComputedStyle {
|
|||||||
pub flex_grow: Option<u16>,
|
pub flex_grow: Option<u16>,
|
||||||
|
|
||||||
pub position: Option<Position>,
|
pub position: Option<Position>,
|
||||||
pub top: Option<f32>,
|
pub top: Option<SizeValue>,
|
||||||
pub right: Option<f32>,
|
pub right: Option<SizeValue>,
|
||||||
pub bottom: Option<f32>,
|
pub bottom: Option<SizeValue>,
|
||||||
pub left: Option<f32>,
|
pub left: Option<SizeValue>,
|
||||||
|
|
||||||
pub overflow_x: Option<Overflow>,
|
pub overflow_x: Option<Overflow>,
|
||||||
pub overflow_y: Option<Overflow>,
|
pub overflow_y: Option<Overflow>,
|
||||||
@@ -862,7 +877,7 @@ pub struct ComputedStyle {
|
|||||||
|
|
||||||
pub opacity: Option<f32>,
|
pub opacity: Option<f32>,
|
||||||
pub font_weight: Option<u16>,
|
pub font_weight: Option<u16>,
|
||||||
pub line_height: Option<f32>,
|
pub line_height: Option<SizeValue>,
|
||||||
pub text_align: Option<TextAlign>,
|
pub text_align: Option<TextAlign>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1196,18 +1211,27 @@ pub fn parse_text_align(s: &str) -> Option<TextAlign> {
|
|||||||
else { None }
|
else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_size(s: &str) -> Option<f32> {
|
pub fn parse_size(s: &str) -> Option<SizeValue> {
|
||||||
let s = s.trim();
|
let s = s.trim();
|
||||||
if s.ends_with('%') || s.eq_ignore_ascii_case("auto") ||
|
if s.eq_ignore_ascii_case("auto") ||
|
||||||
s.eq_ignore_ascii_case("fill") || s.eq_ignore_ascii_case("stretch") {
|
s.eq_ignore_ascii_case("fill") || s.eq_ignore_ascii_case("stretch") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
if s.ends_with('%') {
|
||||||
|
let val = s.trim_end_matches(|c: char| c == '%' || c.is_alphabetic())
|
||||||
|
.parse::<f32>().ok()?;
|
||||||
|
return Some(SizeValue::Percent(val));
|
||||||
|
}
|
||||||
if s.eq_ignore_ascii_case("vw") || s.eq_ignore_ascii_case("vh") {
|
if s.eq_ignore_ascii_case("vw") || s.eq_ignore_ascii_case("vh") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
s.trim_end_matches(|c: char| c.is_alphabetic())
|
let val = s.trim_end_matches(|c: char| c.is_alphabetic())
|
||||||
.parse::<f32>()
|
.parse::<f32>().ok()?;
|
||||||
.ok()
|
Some(SizeValue::Px(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_size(v: Option<SizeValue>, relative_to: Option<f32>) -> Option<f32> {
|
||||||
|
v.map(|sv| sv.resolve(relative_to))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub enum Message {
|
|||||||
ToggleChanged(Option<String>, bool),
|
ToggleChanged(Option<String>, bool),
|
||||||
SliderChanged(Option<String>, f64),
|
SliderChanged(Option<String>, f64),
|
||||||
WindowScrolled(f32),
|
WindowScrolled(f32),
|
||||||
|
ScrollableScrolled(u64, f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
|||||||
190
src/renderer.rs
190
src/renderer.rs
@@ -1,6 +1,6 @@
|
|||||||
use crate::Message;
|
use crate::Message;
|
||||||
use crate::interpreter::Element;
|
use crate::interpreter::Element;
|
||||||
use crate::interpreter::style::{ComputedStyle, ContentAlign, Display, LayoutDirection, Position, Overflow, StructuralContext, StyleSheet, TextAlign};
|
use crate::interpreter::style::{ComputedStyle, ContentAlign, Display, LayoutDirection, Position, Overflow, StructuralContext, StyleSheet, TextAlign, resolve_size};
|
||||||
use iced::widget::container::Style as ContainerStyle;
|
use iced::widget::container::Style as ContainerStyle;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, checkbox, column, container, image, progress_bar, row, scrollable, slider, svg, text,
|
button, checkbox, column, container, image, progress_bar, row, scrollable, slider, svg, text,
|
||||||
@@ -116,8 +116,8 @@ fn make_hoverable<'a>(
|
|||||||
background: style_cs.background.map(Background::Color),
|
background: style_cs.background.map(Background::Color),
|
||||||
border: Border {
|
border: Border {
|
||||||
color: style_cs.border_color.unwrap_or(iced::Color::TRANSPARENT),
|
color: style_cs.border_color.unwrap_or(iced::Color::TRANSPARENT),
|
||||||
width: style_cs.border_width.unwrap_or(0.0),
|
width: resolve_size(style_cs.border_width, None).unwrap_or(0.0),
|
||||||
radius: style_cs.border_radius.unwrap_or(0.0).into(),
|
radius: resolve_size(style_cs.border_radius, None).unwrap_or(0.0).into(),
|
||||||
},
|
},
|
||||||
text_color: style_cs.color.unwrap_or(iced::Color::WHITE),
|
text_color: style_cs.color.unwrap_or(iced::Color::WHITE),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -134,6 +134,8 @@ fn render_children<'a>(
|
|||||||
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
|
scroll_positions: &HashMap<u64, f32>,
|
||||||
|
container_id: u64,
|
||||||
stylesheet: &StyleSheet,
|
stylesheet: &StyleSheet,
|
||||||
) -> Vec<iced::Element<'a, Message, Theme, iced::Renderer>> {
|
) -> Vec<iced::Element<'a, Message, Theme, iced::Renderer>> {
|
||||||
children
|
children
|
||||||
@@ -147,6 +149,8 @@ fn render_children<'a>(
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
abs_layers,
|
abs_layers,
|
||||||
sticky_layers,
|
sticky_layers,
|
||||||
|
scroll_positions,
|
||||||
|
container_id,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -154,13 +158,13 @@ fn render_children<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_padding(cs: &ComputedStyle, default_pad: f32) -> iced::Padding {
|
fn get_padding(cs: &ComputedStyle, default_pad: f32) -> iced::Padding {
|
||||||
let base = cs.padding.unwrap_or(default_pad);
|
let base = resolve_size(cs.padding, None).unwrap_or(default_pad);
|
||||||
let mut top = cs.padding_top.unwrap_or(base);
|
let mut top = resolve_size(cs.padding_top, None).unwrap_or(base);
|
||||||
let mut right = cs.padding_right.unwrap_or(base);
|
let mut right = resolve_size(cs.padding_right, None).unwrap_or(base);
|
||||||
let mut bottom = cs.padding_bottom.unwrap_or(base);
|
let mut bottom = resolve_size(cs.padding_bottom, None).unwrap_or(base);
|
||||||
let mut left = cs.padding_left.unwrap_or(base);
|
let mut left = resolve_size(cs.padding_left, None).unwrap_or(base);
|
||||||
|
|
||||||
let bwidth = cs.border_width.unwrap_or(0.0);
|
let bwidth = resolve_size(cs.border_width, None).unwrap_or(0.0);
|
||||||
|
|
||||||
if let Some(iced::Length::Fixed(w)) = cs.width {
|
if let Some(iced::Length::Fixed(w)) = cs.width {
|
||||||
let total_h = left + right + bwidth * 2.0;
|
let total_h = left + right + bwidth * 2.0;
|
||||||
@@ -189,12 +193,12 @@ fn get_padding(cs: &ComputedStyle, default_pad: f32) -> iced::Padding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_margin(cs: &ComputedStyle) -> iced::Padding {
|
fn get_margin(cs: &ComputedStyle) -> iced::Padding {
|
||||||
let base = cs.margin.unwrap_or(0.0);
|
let base = resolve_size(cs.margin, None).unwrap_or(0.0);
|
||||||
iced::Padding {
|
iced::Padding {
|
||||||
top: cs.margin_top.unwrap_or(base),
|
top: resolve_size(cs.margin_top, None).unwrap_or(base),
|
||||||
right: cs.margin_right.unwrap_or(base),
|
right: resolve_size(cs.margin_right, None).unwrap_or(base),
|
||||||
bottom: cs.margin_bottom.unwrap_or(base),
|
bottom: resolve_size(cs.margin_bottom, None).unwrap_or(base),
|
||||||
left: cs.margin_left.unwrap_or(base),
|
left: resolve_size(cs.margin_left, None).unwrap_or(base),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,8 +227,8 @@ fn get_button_style(cs: &ComputedStyle, status: button::Status) -> button::Style
|
|||||||
background: Some(Background::Color(bg_color)),
|
background: Some(Background::Color(bg_color)),
|
||||||
border: Border {
|
border: Border {
|
||||||
color: cs.border_color.unwrap_or(iced::Color::TRANSPARENT),
|
color: cs.border_color.unwrap_or(iced::Color::TRANSPARENT),
|
||||||
width: cs.border_width.unwrap_or(0.0),
|
width: resolve_size(cs.border_width, None).unwrap_or(0.0),
|
||||||
radius: cs.border_radius.unwrap_or(0.0).into(),
|
radius: resolve_size(cs.border_radius, None).unwrap_or(0.0).into(),
|
||||||
},
|
},
|
||||||
text_color: cs.color.unwrap_or(iced::Color::WHITE),
|
text_color: cs.color.unwrap_or(iced::Color::WHITE),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -241,8 +245,8 @@ fn get_text_input_style(cs: &ComputedStyle) -> text_input::Style {
|
|||||||
color: cs
|
color: cs
|
||||||
.border_color
|
.border_color
|
||||||
.unwrap_or(iced::Color::from_rgb(0.25, 0.25, 0.3)),
|
.unwrap_or(iced::Color::from_rgb(0.25, 0.25, 0.3)),
|
||||||
width: cs.border_width.unwrap_or(1.0),
|
width: resolve_size(cs.border_width, None).unwrap_or(1.0),
|
||||||
radius: cs.border_radius.unwrap_or(0.0).into(),
|
radius: resolve_size(cs.border_radius, None).unwrap_or(0.0).into(),
|
||||||
},
|
},
|
||||||
icon: iced::Color::from_rgb(0.5, 0.5, 0.5),
|
icon: iced::Color::from_rgb(0.5, 0.5, 0.5),
|
||||||
placeholder: iced::Color::from_rgb(0.4, 0.4, 0.45),
|
placeholder: iced::Color::from_rgb(0.4, 0.4, 0.45),
|
||||||
@@ -251,16 +255,39 @@ fn get_text_input_style(cs: &ComputedStyle) -> text_input::Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn estimate_element_height(cs: &ComputedStyle) -> f32 {
|
||||||
|
let pad_top = resolve_size(cs.padding_top.or(cs.padding), None).unwrap_or(0.0);
|
||||||
|
let pad_bottom = resolve_size(cs.padding_bottom.or(cs.padding), None).unwrap_or(0.0);
|
||||||
|
let bwidth = resolve_size(cs.border_width, None).unwrap_or(0.0);
|
||||||
|
let font_size = resolve_size(cs.font_size, None).unwrap_or(16.0);
|
||||||
|
let line_height = resolve_size(cs.line_height, None).unwrap_or(1.2);
|
||||||
|
pad_top + pad_bottom + bwidth * 2.0 + font_size * line_height
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_sticky_position<'a>(
|
||||||
|
widget: iced::Element<'a, Message, Theme, iced::Renderer>,
|
||||||
|
cs: &ComputedStyle,
|
||||||
|
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
||||||
|
let top = resolve_size(cs.top, None).unwrap_or(0.0);
|
||||||
|
container(widget)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.padding(iced::Padding {
|
||||||
|
top,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
fn wrap_fixed_position<'a>(
|
fn wrap_fixed_position<'a>(
|
||||||
widget: iced::Element<'a, Message, Theme, iced::Renderer>,
|
widget: iced::Element<'a, Message, Theme, iced::Renderer>,
|
||||||
cs: &ComputedStyle,
|
cs: &ComputedStyle,
|
||||||
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
||||||
use iced::alignment::{Horizontal, Vertical};
|
use iced::alignment::{Horizontal, Vertical};
|
||||||
|
|
||||||
let top = cs.top.unwrap_or(0.0);
|
let top = resolve_size(cs.top, None).unwrap_or(0.0);
|
||||||
let left = cs.left.unwrap_or(0.0);
|
let left = resolve_size(cs.left, None).unwrap_or(0.0);
|
||||||
let right = cs.right.unwrap_or(0.0);
|
let right = resolve_size(cs.right, None).unwrap_or(0.0);
|
||||||
let bottom = cs.bottom.unwrap_or(0.0);
|
let bottom = resolve_size(cs.bottom, None).unwrap_or(0.0);
|
||||||
|
|
||||||
let has_top = cs.top.is_some();
|
let has_top = cs.top.is_some();
|
||||||
let has_bottom = cs.bottom.is_some();
|
let has_bottom = cs.bottom.is_some();
|
||||||
@@ -300,10 +327,11 @@ fn apply_universal_box_model<'a>(
|
|||||||
cs: &ComputedStyle,
|
cs: &ComputedStyle,
|
||||||
is_window: bool,
|
is_window: bool,
|
||||||
default_padding: f32,
|
default_padding: f32,
|
||||||
|
scrollable_id: Option<u64>,
|
||||||
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
||||||
let bg = cs.background;
|
let bg = cs.background;
|
||||||
let radius = cs.border_radius.unwrap_or(0.0);
|
let radius = resolve_size(cs.border_radius, None).unwrap_or(0.0);
|
||||||
let bwidth = cs.border_width.unwrap_or(0.0);
|
let bwidth = resolve_size(cs.border_width, None).unwrap_or(0.0);
|
||||||
|
|
||||||
let bcolor = cs.border_color.unwrap_or_else(|| {
|
let bcolor = cs.border_color.unwrap_or_else(|| {
|
||||||
if bwidth > 0.0 {
|
if bwidth > 0.0 {
|
||||||
@@ -349,6 +377,10 @@ fn apply_universal_box_model<'a>(
|
|||||||
s = s.height(Length::Fill);
|
s = s.height(Length::Fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(sid) = scrollable_id {
|
||||||
|
s = s.on_scroll(move |viewport| Message::ScrollableScrolled(sid, viewport.absolute_offset().y));
|
||||||
|
}
|
||||||
|
|
||||||
s.into()
|
s.into()
|
||||||
} else {
|
} else {
|
||||||
padded_content.into()
|
padded_content.into()
|
||||||
@@ -386,10 +418,10 @@ fn apply_universal_box_model<'a>(
|
|||||||
if let Some(h) = cs.height {
|
if let Some(h) = cs.height {
|
||||||
inner = inner.height(h);
|
inner = inner.height(h);
|
||||||
}
|
}
|
||||||
if let Some(max_w) = cs.max_width {
|
if let Some(max_w) = resolve_size(cs.max_width, None) {
|
||||||
inner = inner.max_width(max_w);
|
inner = inner.max_width(max_w);
|
||||||
}
|
}
|
||||||
if let Some(max_h) = cs.max_height {
|
if let Some(max_h) = resolve_size(cs.max_height, None) {
|
||||||
inner = inner.max_height(max_h);
|
inner = inner.max_height(max_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,6 +471,8 @@ pub fn render_element<'a>(
|
|||||||
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
|
scroll_positions: &HashMap<u64, f32>,
|
||||||
|
container_id: u64,
|
||||||
stylesheet: &StyleSheet,
|
stylesheet: &StyleSheet,
|
||||||
) -> Option<iced::Element<'a, Message, Theme, iced::Renderer>> {
|
) -> Option<iced::Element<'a, Message, Theme, iced::Renderer>> {
|
||||||
let mut cs = el.computed_style;
|
let mut cs = el.computed_style;
|
||||||
@@ -472,13 +506,13 @@ pub fn render_element<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let current_color = cs.color.or(parent_color);
|
let current_color = cs.color.or(parent_color);
|
||||||
let current_font_size = cs.font_size.or(parent_font_size);
|
let current_font_size = resolve_size(cs.font_size, parent_font_size).or(parent_font_size);
|
||||||
|
|
||||||
if el.type_name == "Window" {
|
if el.type_name == "Window" {
|
||||||
let spacing = cs.spacing.unwrap_or(12.0);
|
let spacing = resolve_size(cs.spacing, None).unwrap_or(12.0);
|
||||||
let mut col = column![].spacing(spacing).width(Length::Fill);
|
let mut col = column![].spacing(spacing).width(Length::Fill);
|
||||||
if let Some(h) = cs.height { col = col.height(h); }
|
|
||||||
|
|
||||||
|
let window_id = el.element_id.0 as u64;
|
||||||
let mut window_fixed_layers = Vec::new();
|
let mut window_fixed_layers = Vec::new();
|
||||||
let mut window_abs_layers = Vec::new();
|
let mut window_abs_layers = Vec::new();
|
||||||
let mut window_sticky_layers = Vec::new();
|
let mut window_sticky_layers = Vec::new();
|
||||||
@@ -491,20 +525,14 @@ pub fn render_element<'a>(
|
|||||||
&mut window_fixed_layers,
|
&mut window_fixed_layers,
|
||||||
&mut window_abs_layers,
|
&mut window_abs_layers,
|
||||||
&mut window_sticky_layers,
|
&mut window_sticky_layers,
|
||||||
|
scroll_positions,
|
||||||
|
window_id,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
) {
|
) {
|
||||||
col = col.push(child);
|
col = col.push(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
let main_flow = apply_universal_box_model(col, &cs, true, 0.0);
|
let main_flow = apply_universal_box_model(col, &cs, true, 0.0, Some(window_id));
|
||||||
|
|
||||||
let has_abs = !window_abs_layers.is_empty();
|
|
||||||
let has_fixed = !window_fixed_layers.is_empty();
|
|
||||||
let has_sticky = !window_sticky_layers.is_empty();
|
|
||||||
|
|
||||||
if !has_abs && !has_fixed && !has_sticky {
|
|
||||||
return Some(main_flow);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut stack_widget = iced::widget::stack![main_flow];
|
let mut stack_widget = iced::widget::stack![main_flow];
|
||||||
for layer in window_abs_layers {
|
for layer in window_abs_layers {
|
||||||
@@ -532,6 +560,8 @@ pub fn render_element<'a>(
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
&mut panel_abs,
|
&mut panel_abs,
|
||||||
&mut panel_sticky,
|
&mut panel_sticky,
|
||||||
|
scroll_positions,
|
||||||
|
container_id,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
);
|
);
|
||||||
if panel_abs.is_empty() {
|
if panel_abs.is_empty() {
|
||||||
@@ -554,6 +584,8 @@ pub fn render_element<'a>(
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
&mut btn_abs,
|
&mut btn_abs,
|
||||||
&mut btn_sticky,
|
&mut btn_sticky,
|
||||||
|
scroll_positions,
|
||||||
|
container_id,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
);
|
);
|
||||||
if btn_abs.is_empty() && btn_sticky.is_empty() {
|
if btn_abs.is_empty() && btn_sticky.is_empty() {
|
||||||
@@ -592,7 +624,7 @@ let element_opt: Option<iced::Element<'a, Message, Theme, iced::Renderer>> =
|
|||||||
widget = widget.align_x(iced::alignment::Horizontal::from(ta));
|
widget = widget.align_x(iced::alignment::Horizontal::from(ta));
|
||||||
widget = widget.width(Length::Fill);
|
widget = widget.width(Length::Fill);
|
||||||
}
|
}
|
||||||
if let Some(lh) = cs.line_height {
|
if let Some(lh) = resolve_size(cs.line_height, current_font_size) {
|
||||||
widget = widget.line_height(lh);
|
widget = widget.line_height(lh);
|
||||||
widget = widget.width(Length::Fill);
|
widget = widget.width(Length::Fill);
|
||||||
widget = widget.wrapping(Wrapping::Word);
|
widget = widget.wrapping(Wrapping::Word);
|
||||||
@@ -617,7 +649,7 @@ let element_opt: Option<iced::Element<'a, Message, Theme, iced::Renderer>> =
|
|||||||
widget = widget.align_x(iced::alignment::Horizontal::from(ta));
|
widget = widget.align_x(iced::alignment::Horizontal::from(ta));
|
||||||
widget = widget.width(Length::Fill);
|
widget = widget.width(Length::Fill);
|
||||||
}
|
}
|
||||||
if let Some(lh) = cs.line_height {
|
if let Some(lh) = resolve_size(cs.line_height, current_font_size) {
|
||||||
widget = widget.line_height(lh);
|
widget = widget.line_height(lh);
|
||||||
widget = widget.width(Length::Fill);
|
widget = widget.width(Length::Fill);
|
||||||
widget = widget.wrapping(Wrapping::Word);
|
widget = widget.wrapping(Wrapping::Word);
|
||||||
@@ -628,9 +660,9 @@ let element_opt: Option<iced::Element<'a, Message, Theme, iced::Renderer>> =
|
|||||||
"Image" => {
|
"Image" => {
|
||||||
let src = el.get_prop("src").unwrap_or("");
|
let src = el.get_prop("src").unwrap_or("");
|
||||||
let path = src.strip_prefix("fs:").unwrap_or(src);
|
let path = src.strip_prefix("fs:").unwrap_or(src);
|
||||||
let radius = cs.border_radius.unwrap_or(0.0);
|
let radius = resolve_size(cs.border_radius, None).unwrap_or(0.0);
|
||||||
let padding = get_padding(&cs, 0.0);
|
let padding = get_padding(&cs, 0.0);
|
||||||
let bwidth = cs.border_width.unwrap_or(0.0);
|
let bwidth = resolve_size(cs.border_width, None).unwrap_or(0.0);
|
||||||
|
|
||||||
let content_w = cs.width.map(|len| {
|
let content_w = cs.width.map(|len| {
|
||||||
if let iced::Length::Fixed(w) = len {
|
if let iced::Length::Fixed(w) = len {
|
||||||
@@ -713,7 +745,7 @@ let element_opt: Option<iced::Element<'a, Message, Theme, iced::Renderer>> =
|
|||||||
if el.children.is_empty() {
|
if el.children.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let spacing = cs.spacing.unwrap_or(10.0);
|
let spacing = resolve_size(cs.spacing, None).unwrap_or(10.0);
|
||||||
let mut col = column![].spacing(spacing);
|
let mut col = column![].spacing(spacing);
|
||||||
let mut default_abs = Vec::new();
|
let mut default_abs = Vec::new();
|
||||||
let mut default_sticky = Vec::new();
|
let mut default_sticky = Vec::new();
|
||||||
@@ -725,6 +757,8 @@ let element_opt: Option<iced::Element<'a, Message, Theme, iced::Renderer>> =
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
&mut default_abs,
|
&mut default_abs,
|
||||||
&mut default_sticky,
|
&mut default_sticky,
|
||||||
|
scroll_positions,
|
||||||
|
container_id,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
) {
|
) {
|
||||||
col = col.push(child);
|
col = col.push(child);
|
||||||
@@ -740,24 +774,23 @@ let element_opt: Option<iced::Element<'a, Message, Theme, iced::Renderer>> =
|
|||||||
}
|
}
|
||||||
stack.into()
|
stack.into()
|
||||||
};
|
};
|
||||||
let boxed = apply_universal_box_model(content_widget, &cs, false, 0.0);
|
let is_scrollable = cs.overflow_y.map(|o| o == Overflow::Scroll || o == Overflow::Auto).unwrap_or(false);
|
||||||
// Sticky outside (after box model = outside scrollable)
|
let sc_id: Option<u64> = if is_scrollable { Some(el.element_id.0 as u64) } else { None };
|
||||||
if default_sticky.is_empty() {
|
let boxed = apply_universal_box_model(content_widget, &cs, false, 0.0, sc_id);
|
||||||
final_widget_opt = Some(boxed);
|
|
||||||
} else {
|
|
||||||
let mut stack = iced::widget::stack![boxed];
|
let mut stack = iced::widget::stack![boxed];
|
||||||
for layer in default_sticky {
|
for layer in default_sticky {
|
||||||
stack = stack.push(layer);
|
stack = stack.push(layer);
|
||||||
}
|
}
|
||||||
final_widget_opt = Some(stack.into());
|
final_widget_opt = Some(stack.into());
|
||||||
}
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(element) = element_opt {
|
if let Some(element) = element_opt {
|
||||||
final_widget_opt = Some(apply_universal_box_model(element, &cs, false, 0.0));
|
let is_scrollable = cs.overflow_y.map(|o| o == Overflow::Scroll || o == Overflow::Auto).unwrap_or(false);
|
||||||
|
let sc_id: Option<u64> = if is_scrollable { Some(el.element_id.0 as u64) } else { None };
|
||||||
|
final_widget_opt = Some(apply_universal_box_model(element, &cs, false, 0.0, sc_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -778,9 +811,21 @@ let element_opt: Option<iced::Element<'a, Message, Theme, iced::Renderer>> =
|
|||||||
abs_layers.push(wrapped);
|
abs_layers.push(wrapped);
|
||||||
None
|
None
|
||||||
} else if is_sticky {
|
} else if is_sticky {
|
||||||
let wrapped = wrap_fixed_position(final_widget, &cs);
|
let threshold = resolve_size(cs.top, None).unwrap_or(0.0);
|
||||||
|
let scroll_y = scroll_positions.get(&container_id).copied().unwrap_or(0.0);
|
||||||
|
if scroll_y > threshold {
|
||||||
|
let wrapped = wrap_sticky_position(final_widget, &cs);
|
||||||
sticky_layers.push(wrapped);
|
sticky_layers.push(wrapped);
|
||||||
None
|
let spacer_h = estimate_element_height(&cs);
|
||||||
|
let spacer: iced::Element<'a, Message, Theme, iced::Renderer> =
|
||||||
|
container(iced::widget::text(""))
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fixed(spacer_h))
|
||||||
|
.into();
|
||||||
|
Some(spacer)
|
||||||
|
} else {
|
||||||
|
Some(final_widget)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(final_widget)
|
Some(final_widget)
|
||||||
}
|
}
|
||||||
@@ -797,9 +842,11 @@ fn render_panel<'a>(
|
|||||||
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
|
scroll_positions: &HashMap<u64, f32>,
|
||||||
|
container_id: u64,
|
||||||
stylesheet: &StyleSheet,
|
stylesheet: &StyleSheet,
|
||||||
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
||||||
let spacing = cs.spacing.unwrap_or(10.0);
|
let spacing = resolve_size(cs.spacing, None).unwrap_or(10.0);
|
||||||
|
|
||||||
let is_horizontal = el.children.iter().all(|c| {
|
let is_horizontal = el.children.iter().all(|c| {
|
||||||
matches!(
|
matches!(
|
||||||
@@ -814,6 +861,10 @@ let is_horizontal = el.children.iter().all(|c| {
|
|||||||
LayoutDirection::Column
|
LayoutDirection::Column
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let is_scrollable = cs.overflow_y.map(|o| o == Overflow::Scroll || o == Overflow::Auto).unwrap_or(false);
|
||||||
|
let child_container: u64 = if is_scrollable { el.element_id.0 as u64 } else { container_id };
|
||||||
|
let sc_id: Option<u64> = if is_scrollable { Some(el.element_id.0 as u64) } else { None };
|
||||||
|
|
||||||
let content: iced::Element<'a, Message, Theme, iced::Renderer> = match direction {
|
let content: iced::Element<'a, Message, Theme, iced::Renderer> = match direction {
|
||||||
LayoutDirection::Row => {
|
LayoutDirection::Row => {
|
||||||
let mut r = row![].spacing(spacing);
|
let mut r = row![].spacing(spacing);
|
||||||
@@ -830,6 +881,8 @@ let is_horizontal = el.children.iter().all(|c| {
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
abs_layers,
|
abs_layers,
|
||||||
sticky_layers,
|
sticky_layers,
|
||||||
|
scroll_positions,
|
||||||
|
child_container,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
) {
|
) {
|
||||||
r = r.push(child);
|
r = r.push(child);
|
||||||
@@ -858,6 +911,8 @@ let is_horizontal = el.children.iter().all(|c| {
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
abs_layers,
|
abs_layers,
|
||||||
sticky_layers,
|
sticky_layers,
|
||||||
|
scroll_positions,
|
||||||
|
child_container,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
) {
|
) {
|
||||||
c = c.push(child);
|
c = c.push(child);
|
||||||
@@ -897,6 +952,8 @@ let is_horizontal = el.children.iter().all(|c| {
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
&mut chunk_abs,
|
&mut chunk_abs,
|
||||||
&mut chunk_sticky,
|
&mut chunk_sticky,
|
||||||
|
scroll_positions,
|
||||||
|
child_container,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
) {
|
) {
|
||||||
r = r.push(child);
|
r = r.push(child);
|
||||||
@@ -918,18 +975,14 @@ let is_horizontal = el.children.iter().all(|c| {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let boxed = apply_universal_box_model(content, &cs, false, 5.0);
|
let boxed = apply_universal_box_model(content, &cs, false, 5.0, sc_id);
|
||||||
|
|
||||||
if sticky_layers.is_empty() {
|
|
||||||
boxed
|
|
||||||
} else {
|
|
||||||
let layers = std::mem::take(sticky_layers);
|
let layers = std::mem::take(sticky_layers);
|
||||||
let mut stack = iced::widget::stack![boxed];
|
let mut stack = iced::widget::stack![boxed];
|
||||||
for layer in layers {
|
for layer in layers {
|
||||||
stack = stack.push(layer);
|
stack = stack.push(layer);
|
||||||
}
|
}
|
||||||
stack.into()
|
stack.into()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_button<'a>(
|
fn render_button<'a>(
|
||||||
@@ -940,6 +993,8 @@ fn render_button<'a>(
|
|||||||
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
fixed_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
abs_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
sticky_layers: &mut Vec<iced::Element<'a, Message, Theme, iced::Renderer>>,
|
||||||
|
scroll_positions: &HashMap<u64, f32>,
|
||||||
|
container_id: u64,
|
||||||
stylesheet: &StyleSheet,
|
stylesheet: &StyleSheet,
|
||||||
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
) -> iced::Element<'a, Message, Theme, iced::Renderer> {
|
||||||
let final_color = cs.color.or(parent_color);
|
let final_color = cs.color.or(parent_color);
|
||||||
@@ -961,6 +1016,8 @@ fn render_button<'a>(
|
|||||||
fixed_layers,
|
fixed_layers,
|
||||||
abs_layers,
|
abs_layers,
|
||||||
sticky_layers,
|
sticky_layers,
|
||||||
|
scroll_positions,
|
||||||
|
container_id,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
) {
|
) {
|
||||||
r = r.push(child);
|
r = r.push(child);
|
||||||
@@ -979,20 +1036,15 @@ fn render_button<'a>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let base_px = resolve_size(cs.padding, None).unwrap_or(8.0);
|
||||||
let mut padding = iced::Padding {
|
let mut padding = iced::Padding {
|
||||||
top: cs.padding_top.or(cs.padding).unwrap_or(8.0),
|
top: resolve_size(cs.padding_top, None).unwrap_or(base_px),
|
||||||
bottom: cs.padding_bottom.or(cs.padding).unwrap_or(8.0),
|
bottom: resolve_size(cs.padding_bottom, None).unwrap_or(base_px),
|
||||||
left: cs
|
left: resolve_size(cs.padding_left, None).unwrap_or(base_px * 2.0),
|
||||||
.padding_left
|
right: resolve_size(cs.padding_right, None).unwrap_or(base_px * 2.0),
|
||||||
.or_else(|| cs.padding.map(|p| p * 2.0))
|
|
||||||
.unwrap_or(16.0),
|
|
||||||
right: cs
|
|
||||||
.padding_right
|
|
||||||
.or_else(|| cs.padding.map(|p| p * 2.0))
|
|
||||||
.unwrap_or(16.0),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let bwidth = cs.border_width.unwrap_or(0.0);
|
let bwidth = resolve_size(cs.border_width, None).unwrap_or(0.0);
|
||||||
if let Some(iced::Length::Fixed(w)) = cs.width {
|
if let Some(iced::Length::Fixed(w)) = cs.width {
|
||||||
let total_h = padding.left + padding.right + bwidth * 2.0;
|
let total_h = padding.left + padding.right + bwidth * 2.0;
|
||||||
if total_h > w && w > 0.0 {
|
if total_h > w && w > 0.0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user