Add circular progress bar widget for Gtk4
Introduce a new circular progress bar widget for GTK4
[!CAUTION] It's necessary to link the math library to use this widget.
This explains
gtk4/src/meson.build, new line:link_args: ['-lm']
I don't use it directly, but the widget uses it under the hood.
This widget has 4 Widgets which are:
- Progress Arc, the most important one:
double percentage[0..1], tells how much is drawn.int line_widthhow thick the arc isGsk.LineCap line_capthe line cap style, Rounded, Square, Butt.Gsk.FillRule fill_rulethe fill rule, EvenOdd, Winding.
- Radius Fill, the radius fill area.
bool radius_filledwhether the radius is filled.- ProgressArc props are also used to draw the radius.
- Center Fill, the center fill area.
bool center_filledwhether the center is filled.
- Child, just a normal Gtk.Widget. It can be anything.
Css nodes
circular-progress
├── progress-arc
├── center-fill
╰── radius-fill
Use the CSS color property to set the color of each node.
Example
With Gtk Blueprint:
$CircularProgressBar {
percentage: bind template.speaker as <AstalWp.Endpoint>.volume;
line-width: 5;
line-cap: round;
center-filled: false;
radius-filled: true;
child: Image {
icon-name: bind template.speaker as <AstalWp.Endpoint>.volume-icon;
};
}
The icon looks unimpressive, but it's not an issue with the CircularProgressBar.
I haven't looked at the widget implementation yet.
Please also add it to the jsx-runtime so you can use it in the gjs bindings using jsx. Note that in gtk4 astal makes use of Gtk.Builder to build the widget tree from jsx, so you need to inherit from Gtk.Buildable and at least implement its add_child method.
This would then also allow to this in blueprint instead of specifying the child prop:
$CircularProgressBar {
percentage: bind template.speaker as <AstalWp.Endpoint>.volume;
line-width: 5;
line-cap: round;
center-filled: false;
radius-filled: true;
Image {
icon-name: bind template.speaker as <AstalWp.Endpoint>.volume-icon;
};
}
This would then also allow to this in blueprint instead of specifying the child prop:
It implements Gtk.Buildable, and Gtk.Buildable.add_child:
Astal.CircularProgressBar {
percentage: bind template.speaker as <AstalWp.Endpoint>.volume;
line-width: 5;
line-cap: round;
center-filled: true;
radius-filled: true;
fill-rule: winding;
Image {
icon-name: bind template.speaker as <AstalWp.Endpoint>.volume-icon;
}
}
I have added GJS Binding, which I think they're correct, suggestion/corrections would be great. Valid snippet:
<CircularProgress
lineCap={Gsk.LineCap.ROUND}
lineWidth={5}
percentage={0.5}
centerFilled={true}
radiusFilled={true}
fillRule={Gsk.FillRule.EVEN_ODD}
widthRequest={30}
><image
iconName={"audio-volume-high-symbolic"}
/>
</CircularProgress>
I will implement all or most of the changes. Nevertheless, I will put this widget on standby, as a hypothetical Astal.Gizmo seems more suitable to create first. Then, I will use it to develop a circular progress widget.
Changes made:
- Added circularprogress to the JSX runtime in the jsx-runtime.ts file.
- Added Gsk to the reexports in the index.ts file.
- Removed cached values(radius, delta, width, height), unnecessary "optimization"
- Removed private properties,
bool _center_filled,bool _radius_filled,Gsk.LineCap _line_cap,Gsk.FillRule _fill_rule, and used public variable directly for most cases. - Removed
queue_draw()calls on every setter and moved to the constructor, called on each property change. - Used base.add_child instead of handling event controllers manually.
- Changed CSS Nodes to
circularprogress,progress,radius, andcenterto make it consistent and simpler. - Removed unused
measure()method as the CircularProgress uses BinLayout as LayoutManager - Removed LayoutManager to the different nodes of CircularProgress, as these don't and won't have children.
- Changed and formatted the code style to make it subjectively more readable and consistent with the rest of the code.
[!WARNING] This still doesn't use Astal.Gizmo
[!WARNING] Below will only happen if Astal.Gizmo or similar is merged.
I’ve created a variant of this widget using Astal.Gizmo from #293 , which you can find here. If the Gizmo gets approved, I’ll use that version and force merge the branch into this one.
I totally missed that this implementation is missing a feature which the gtk3 version has, and many people probably want.
The gtk3 version allows to specify a start and end point, so it does not draw a full circle, but only an arc.
You could then also make a function which draws an arc, which gets used by both the radius and progress fill snapshot functions to deduplicate code.
I totally missed that this implementation is missing a feature which the gtk3 version has, and many people probably want.
The gtk3 version allows to specify a start and end point, so it does not draw a full circle, but only an arc.
You could then also make a function which draws an arc, which gets used by both the radius and progress fill snapshot functions to deduplicate code.
I have implemented the start_at and end_at properties, both works. Also implemented the inverted property, so now you can draw a progress bar clockwise and counter-clockwise. I have refactored the snapshot of the progressArc, to be more "readable".
Both start_at and end_at can have values from -1 to 1, where:
-1 => -2π0 => 01 => 2π
Kinda obvious. Also, there can be a lot of refactoring, like you said having a draw arc for both radius and progress, could be possible, but some issues exist when trying to draw a complete arc, which is easily avoidable by drawing a circle, and cleaner. Using the Astal.Gizmo offers a lot of room for refactoring too.
Hello and first thank you for your work.
I was planning to migrate my config to gtk4 but the absence of this widget block me :sweat:.
I see that #293 is quite old. Is #293 still plan ? If not can we merge this pr in the current state ?
NB: I totally understand that this is an opensource project without any guaranteed. this message is just to understand current status.
I see that #293 is quite old. Is #293 still plan ? If not can we merge this pr in the current state ?
I use that gizmo every day. I think it is just a simple widget that doesn't add any overhead and acts as a helper. On the other hand, I consider that this PR, in its current state, is obsolete or at least not the best approach, since I use a variant based on the gizmo from #293, which I believe looks cleaner. So, merging Astal.Gizmo would simplify this widget's structure.
I have completely refactored the widget with the recent merge of #293, so once again, this widget is ready for review.