feat: borders and rounding

This commit is contained in:
Ashley Wulber 2026-04-06 23:46:13 -04:00
parent 6a48eae8e0
commit 1896cb01d9
5 changed files with 135 additions and 25 deletions

View file

@ -230,6 +230,7 @@ impl cosmic::Application for App {
)
.push(
widget::progress_bar::circular::Circular::new()
.bar_height(10.0)
.size(50.0)
.progress(self.progress),
)

View file

@ -306,6 +306,33 @@ where
);
if let Some(progress) = self.progress {
// outer border
if let Some(border_color) = custom_style.border_color {
let border_path =
canvas::Path::circle(frame.center(), track_radius + self.bar_height / 2.0);
frame.stroke(
&border_path,
canvas::Stroke::default()
.with_color(border_color)
.with_width(1.0),
);
}
// inner border
if let Some(border_color) = custom_style.border_color {
let border_path =
canvas::Path::circle(frame.center(), track_radius - self.bar_height / 2.0);
frame.stroke(
&border_path,
canvas::Stroke::default()
.with_color(border_color)
.with_width(1.0),
);
}
// bar
let mut builder = canvas::path::Builder::new();
builder.arc(canvas::path::Arc {
@ -323,29 +350,53 @@ where
.with_color(custom_style.bar_color)
.with_width(self.bar_height),
);
let mut builder = canvas::path::Builder::new();
// get center of end of arc for rounded cap
let end_angle = -PI / 2.0 + progress * 2.0 * PI;
let end_center =
frame.center() + Vector::new(end_angle.cos(), end_angle.sin()) * track_radius;
builder.arc(canvas::path::Arc {
center: end_center,
radius: self.bar_height / 2.0,
start_angle: Radians(end_angle),
end_angle: Radians(end_angle + PI),
});
// get center of start of arc for rounded cap
let start_angle = -PI / 2.0;
let start_center = frame.center()
+ Vector::new(start_angle.cos(), start_angle.sin()) * track_radius;
builder.arc(canvas::path::Arc {
center: start_center,
radius: self.bar_height / 2.0,
start_angle: Radians(start_angle - PI),
end_angle: Radians(start_angle),
});
let cap_path = builder.build();
frame.fill(&cap_path, custom_style.bar_color);
} else {
let mut builder = canvas::path::Builder::new();
let start = Radians(state.animation.rotation() * 2.0 * PI);
match state.animation {
Animation::Expanding { progress, .. } => {
builder.arc(canvas::path::Arc {
center: frame.center(),
radius: track_radius,
start_angle: start,
end_angle: start + MIN_ANGLE + WRAP_ANGLE * (smootherstep(progress)),
});
}
Animation::Contracting { progress, .. } => {
builder.arc(canvas::path::Arc {
center: frame.center(),
radius: track_radius,
start_angle: start + WRAP_ANGLE * (smootherstep(progress)),
end_angle: start + MIN_ANGLE + WRAP_ANGLE,
});
}
}
let (start_angle, end_angle) = match state.animation {
Animation::Expanding { progress, .. } => (
start,
start + MIN_ANGLE + WRAP_ANGLE * (smootherstep(progress)),
),
Animation::Contracting { progress, .. } => (
start + WRAP_ANGLE * (smootherstep(progress)),
start + MIN_ANGLE + WRAP_ANGLE,
),
};
builder.arc(canvas::path::Arc {
center: frame.center(),
radius: track_radius,
start_angle,
end_angle,
});
let bar_path = builder.build();
@ -355,6 +406,31 @@ where
.with_color(custom_style.bar_color)
.with_width(self.bar_height),
);
let mut builder = canvas::path::Builder::new();
// get center of end of arc for rounded cap
let end_center = frame.center()
+ Vector::new(end_angle.0.cos(), end_angle.0.sin()) * track_radius;
builder.arc(canvas::path::Arc {
center: end_center,
radius: self.bar_height / 2.0,
start_angle: Radians(end_angle.0),
end_angle: Radians(end_angle.0 + PI),
});
// get center of start of arc for rounded cap
let start_center = frame.center()
+ Vector::new(start_angle.0.cos(), start_angle.0.sin()) * track_radius;
builder.arc(canvas::path::Arc {
center: start_center,
radius: self.bar_height / 2.0,
start_angle: Radians(start_angle.0 - PI),
end_angle: Radians(start_angle.0),
});
let cap_path = builder.build();
frame.fill(&cap_path, custom_style.bar_color);
}
});

View file

@ -216,6 +216,16 @@ where
width: bounds.width,
height: bounds.height,
},
border: iced::Border {
width: if custom_style.border_color.is_some() {
1.0
} else {
0.0
},
color: custom_style.border_color.unwrap_or(custom_style.bar_color),
radius: custom_style.border_radius.into(),
},
snap: true,
..renderer::Quad::default()
},
Background::Color(custom_style.track_color),
@ -230,6 +240,12 @@ where
width: progress * bounds.width,
height: bounds.height,
},
border: iced::Border {
width: 0.,
color: iced::Color::TRANSPARENT,
radius: custom_style.border_radius.into(),
},
snap: true,
..renderer::Quad::default()
},
Background::Color(custom_style.bar_color),
@ -244,6 +260,12 @@ where
width: smootherstep(*progress) * bounds.width,
height: bounds.height,
},
border: iced::Border {
width: 0.,
color: iced::Color::TRANSPARENT,
radius: custom_style.border_radius.into(),
},
snap: true,
..renderer::Quad::default()
},
Background::Color(custom_style.bar_color),
@ -257,6 +279,12 @@ where
width: (1.0 - smootherstep(*progress)) * bounds.width,
height: bounds.height,
},
border: iced::Border {
width: 0.,
color: iced::Color::TRANSPARENT,
radius: custom_style.border_radius.into(),
},
snap: true,
..renderer::Quad::default()
},
Background::Color(custom_style.bar_color),

View file

@ -8,6 +8,8 @@ pub struct Appearance {
pub bar_color: Color,
/// The border [`Color`] of the progress indicator.
pub border_color: Option<Color>,
/// The border radius of the progress indicator.
pub border_radius: f32,
}
impl std::default::Default for Appearance {
@ -16,6 +18,7 @@ impl std::default::Default for Appearance {
track_color: Color::TRANSPARENT,
bar_color: Color::BLACK,
border_color: None,
border_radius: 0.0,
}
}
}
@ -49,6 +52,7 @@ impl StyleSheet for iced::Theme {
track_color: palette.background.weak.color,
bar_color: palette.primary.base.color,
border_color: None,
border_radius: 0.0,
}
}
}
@ -63,7 +67,8 @@ impl StyleSheet for crate::Theme {
is_circular: bool,
) -> Appearance {
let cur = self.current_container();
let cur_divider = cur.divider;
let mut cur_divider = cur.divider;
cur_divider.alpha = 0.5;
let theme = self.cosmic();
let (mut track_color, bar_color) = if theme.is_dark && theme.is_high_contrast {
@ -94,6 +99,7 @@ impl StyleSheet for crate::Theme {
} else {
None
},
border_radius: theme.corner_radii.radius_xl[0],
}
}
}

View file

@ -262,10 +262,10 @@ where
font.hash(&mut hasher);
let text_hash = hasher.finish();
if let Some(prev_hash) = state.text_hashes.insert(key, text_hash) {
if prev_hash == text_hash {
return;
}
if let Some(prev_hash) = state.text_hashes.insert(key, text_hash)
&& prev_hash == text_hash
{
return;
}
if let Some(paragraph) = state.paragraphs.get_mut(key) {
@ -928,7 +928,6 @@ where
fn diff(&mut self, tree: &mut Tree) {
let state = tree.state.downcast_mut::<LocalState>();
for key in self.model.order.iter().copied() {
self.update_entity_paragraph(state, key);
}