1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-02 11:11:04 +02:00

Merge branch 'master' into feat/networkmanager

This commit is contained in:
Reinout Meliesie 2024-04-18 01:38:16 +02:00
commit b860f5b603
Signed by: zedfrigg
GPG key ID: 3AFCC06481308BC6
57 changed files with 2450 additions and 858 deletions

View file

@ -49,8 +49,8 @@ dnf install libpulseaudio-devel
By default, all features are enabled for convenience. This can result in a significant compile time.
If you know you are not going to need all the features, you can compile with only the features you need.
As of `v0.10.0`, compiling with no features is about 33% faster.
On a 3800X, it takes about 60 seconds for no features and 90 seconds for all.
As of `v0.15.0`, compiling with no features is about 50% faster.
On a 3800X, it takes about 45 seconds for no features and 90 seconds for all.
This difference is expected to increase as the bar develops.
Features containing a `+` can be stacked, for example `config+json` and `config+yaml` could both be enabled.
@ -77,6 +77,7 @@ cargo build --release --no-default-features \
| config+corn | Enables configuration support for [Corn](https://github.com/jakestanger/corn). |
| config+ron | Enables configuration support for [Ron](https://github.com/ron-rs/ron). |
| **Modules** | |
| cairo | Enables the `cairo` module |
| clipboard | Enables the `clipboard` module. |
| clock | Enables the `clock` module. |
| focused | Enables the `focused` module. |
@ -84,6 +85,7 @@ cargo build --release --no-default-features \
| music+all | Enables the `music` module with support for all player types. |
| music+mpris | Enables the `music` module with MPRIS support. |
| music+mpd | Enables the `music` module with MPD support. |
| notifications | Enables the `notiications` module. |
| sys_info | Enables the `sys_info` module. |
| tray | Enables the `tray` module. |
| upower | Enables the `upower` module. |

View file

@ -332,6 +332,7 @@ For information on the `Script` type, and embedding scripts in strings, see [her
| `show_if` | [Dynamic Boolean](dynamic-values#dynamic-boolean) | `null` | Polls the script to check its exit code. If exit code is zero, the module is shown. For other codes, it is hidden. |
| `transition_type` | `slide_start` or `slide_end` or `crossfade` or `none` | `slide_start` | The transition animation to use when showing/hiding the widget. |
| `transition_duration` | `integer` | `250` | The length of the transition animation to use when showing/hiding the widget. |
| `disable_popup` | `boolean` | `false` | Prevents the popup from opening on-click for this widget. |
#### Appearance

View file

@ -79,7 +79,7 @@ Responds with `ok`.
### `get`
Gets an [ironvar](ironvars) value.
Gets an [ironvar](ironvars) value.
Responds with `ok_value` if the value exists, otherwise `error`.
@ -104,6 +104,20 @@ Responds with `ok`.
}
```
### list
Gets a list of all [ironvar](ironvars) values.
Responds with `ok_value`.
Each key/value pair is on its own `\n` separated newline. The key and value are separated by a colon and space `: `.
```json
{
"type": "list"
}
```
### `load_css`
Loads an additional CSS stylesheet, with hot-reloading enabled.

View file

@ -20,9 +20,11 @@
## Custom
- [Power Menu](power-menu)
- [Weather](weather)
# Modules
- [Cairo](cairo)
- [Clipboard](clipboard)
- [Clock](clock)
- [Custom](custom)

View file

@ -0,0 +1,468 @@
Creates a button on the bar which displays the current weather condition and temperature.
Clicking the button opens a popup with forecast information for the next few days.
Weather information is fetched from [wttr.in](https://wttr.in) via an external script.
You will need to set up the script to be run as a service.
![custom weather widget, with popup open](https://f.jstanger.dev/github/ironbar/custom-weather.png)
## Configuration
<details>
<summary>JSON</summary>
```json
{
"end": [
{
"type": "custom",
"class": "weather",
"bar": [
{
"type": "button",
"label": "#weather_current",
"on_click": "popup:toggle"
}
],
"popup": [
{
"type": "box",
"orientation": "vertical",
"widgets": [
{
"type": "label",
"name": "header",
"label": "Forecast"
},
{
"type": "box",
"widgets": [
{
"type": "box",
"name": "dates",
"orientation": "vertical",
"widgets": [
{
"type": "label",
"class": "weather-date",
"label": "#weather_date_0"
},
{
"type": "label",
"class": "weather-date",
"label": "#weather_date_1"
},
{
"type": "label",
"class": "weather-date",
"label": "#weather_date_2"
}
]
},
{
"type": "box",
"name": "temps",
"orientation": "vertical",
"widgets": [
{
"type": "box",
"widgets": [
{
"type": "label",
"class": "weather-high",
"label": " #weather_high_0"
},
{
"type": "label",
"class": "weather-avg",
"label": " #weather_avg_0"
},
{
"type": "label",
"class": "weather-low",
"label": " #weather_low_0"
}
]
},
{
"type": "box",
"widgets": [
{
"type": "label",
"class": "weather-high",
"label": " #weather_high_1"
},
{
"type": "label",
"class": "weather-avg",
"label": " #weather_avg_1"
},
{
"type": "label",
"class": "weather-low",
"label": " #weather_low_1"
}
]
},
{
"type": "box",
"widgets": [
{
"type": "label",
"class": "weather-high",
"label": " #weather_high_2"
},
{
"type": "label",
"class": "weather-avg",
"label": " #weather_avg_2"
},
{
"type": "label",
"class": "weather-low",
"label": " #weather_low_2"
}
]
}
]
}
]
}
]
}
]
}
]
}
```
</details>
<details>
<summary>TOML</summary>
```toml
[[end]]
type = "custom"
class = "weather"
[[end.bar]]
type = "button"
label = "#weather_current"
on_click = "popup:toggle"
[[end.popup]]
type = "box"
orientation = "vertical"
[[end.popup.widgets]]
type = "label"
name = "header"
label = "Forecast"
[[end.popup.widgets]]
type = "box"
[[end.popup.widgets.widgets]]
type = "box"
name = "dates"
orientation = "vertical"
[[end.popup.widgets.widgets.widgets]]
type = "label"
class = "weather-date"
label = "#weather_date_0"
[[end.popup.widgets.widgets.widgets]]
type = "label"
class = "weather-date"
label = "#weather_date_1"
[[end.popup.widgets.widgets.widgets]]
type = "label"
class = "weather-date"
label = "#weather_date_2"
[[end.popup.widgets.widgets]]
type = "box"
name = "temps"
orientation = "vertical"
[[end.popup.widgets.widgets.widgets]]
type = "box"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-high"
label = " #weather_high_0"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-avg"
label = " #weather_avg_0"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-low"
label = " #weather_low_0"
[[end.popup.widgets.widgets.widgets]]
type = "box"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-high"
label = " #weather_high_1"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-avg"
label = " #weather_avg_1"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-low"
label = " #weather_low_1"
[[end.popup.widgets.widgets.widgets]]
type = "box"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-high"
label = " #weather_high_2"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-avg"
label = " #weather_avg_2"
[[end.popup.widgets.widgets.widgets.widgets]]
type = "label"
class = "weather-low"
label = " #weather_low_2"
```
</details>
<details>
<summary>YAML</summary>
```yaml
end:
- type: custom
class: weather
bar:
- type: button
label: '#weather_current'
on_click: popup:toggle
popup:
- type: box
orientation: vertical
widgets:
- type: label
name: header
label: Forecast
- type: box
widgets:
- type: box
name: dates
orientation: vertical
widgets:
- type: label
class: weather-date
label: '#weather_date_0'
- type: label
class: weather-date
label: '#weather_date_1'
- type: label
class: weather-date
label: '#weather_date_2'
- type: box
name: temps
orientation: vertical
widgets:
- type: box
widgets:
- type: label
class: weather-high
label: ' #weather_high_0'
- type: label
class: weather-avg
label: ' #weather_avg_0'
- type: label
class: weather-low
label: ' #weather_low_0'
- type: box
widgets:
- type: label
class: weather-high
label: ' #weather_high_1'
- type: label
class: weather-avg
label: ' #weather_avg_1'
- type: label
class: weather-low
label: ' #weather_low_1'
- type: box
widgets:
- type: label
class: weather-high
label: ' #weather_high_2'
- type: label
class: weather-avg
label: ' #weather_avg_2'
- type: label
class: weather-low
label: ' #weather_low_2'
```
</details>
<details>
<summary>Corn</summary>
```corn
let {
$weather = {
type = "custom"
class = "weather"
bar = [ { type = "button" label = "#weather_current" on_click = "popup:toggle" } ]
popup = [ {
type = "box"
orientation = "vertical"
widgets = [
{ type = "label" name = "header" label = "Forecast" }
{
type = "box"
widgets = [
{ type = "box" name="dates" orientation = "vertical" widgets = [
{ type = "label" class="weather-date" label = "#weather_date_0" }
{ type = "label" class="weather-date" label = "#weather_date_1" }
{ type = "label" class="weather-date" label = "#weather_date_2" }
]}
{ type = "box" name="temps" orientation = "vertical" widgets = [
{
type = "box"
widgets = [
{ type = "label" class="weather-high" label = " #weather_high_0" }
{ type = "label" class="weather-avg" label = " #weather_avg_0" }
{ type = "label" class="weather-low" label = " #weather_low_0" }
]
}
{
type = "box"
widgets = [
{ type = "label" class="weather-high" label = " #weather_high_1" }
{ type = "label" class="weather-avg" label = " #weather_avg_1" }
{ type = "label" class="weather-low" label = " #weather_low_1" }
]
}
{
type = "box"
widgets = [
{ type = "label" class="weather-high" label = " #weather_high_2" }
{ type = "label" class="weather-avg" label = " #weather_avg_2" }
{ type = "label" class="weather-low" label = " #weather_low_2" }
]
}
] }
]
}
]
} ]
}
} in {
end = [ $weather ]
}
```
</details>
## Script
Run the following script on a timer. Ensure to fill out your city name.
```js
#!/usr/bin/env zx
const location = "Canterbury";
// JS uses Sunday as first day
const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
// bar logic
const data = await fetch(`https://wttr.in/${location}?format=%c %t|%m %t|%S|%s`)
.then(r => r.text());
const [day, night, sunrise, sunset] = data.replaceAll("+", "").split("|");
const [sunriseH, sunriseM, sunriseS] = sunrise.split(":");
const [sunsetH, sunsetM, sunsetS] = sunset.split(":");
const currentTime = new Date();
const sunriseTime = new Date(currentTime);
sunriseTime.setHours(sunriseH);
sunriseTime.setMinutes(sunriseM);
sunriseTime.setSeconds(sunriseS);
const sunsetTime = new Date(currentTime);
sunsetTime.setHours(sunsetH);
sunsetTime.setMinutes(sunsetM);
sunsetTime.setSeconds(sunsetS);
let value = day;
if(currentTime < sunriseTime || currentTime > sunsetTime) value = night;
await $`ironbar set weather_current ${value}`;
// popup logic
const forecast = await fetch(`https://wttr.in/${location}?format=j1`).then(r => r.json());
for (const i in forecast.weather) {
const report = forecast.weather[i];
const day = weekday[new Date(report.date).getDay()];
await $`ironbar set weather_date_${i} ${day}`;
await $`ironbar set weather_avg_${i} ${report.avgtempC.padStart(2, "0")}`;
await $`ironbar set weather_high_${i} ${report.maxtempC.padStart(2, "0")}`;
await $`ironbar set weather_low_${i} ${report.mintempC.padStart(2, "0")}`;
}
```
## Styling
```css
.popup-weather #header {
font-size: 1.8em;
padding-bottom: 0.4em;
margin-bottom: 0.6em;
border-bottom: 1px solid @color-border;
}
.popup-weather .weather-date {
font-size: 1.5em;
padding-right: 1em;
}
.popup-weather .weather-avg {
margin-left: 0.5em;
margin-right: 0.5em;
}
/*
this is a hack to align the different font sizes on left/right
you may need to adjust for different fonts
*/
.popup-weather #temps label {
padding-top: 0.2em;
margin-bottom: 0.7em;
}
```

215
docs/modules/Cairo.md Normal file
View file

@ -0,0 +1,215 @@
Allows you to render custom content using the Lua and the Cairo drawing library.
This is an advanced feature which provides a powerful escape hatch, allowing you to fetch data and render anything
using an embedded scripting environment.
Scripts are automatically hot-reloaded.
> [!NOTE]
> The Lua engine uses LuaJIT 5.1, and requires the use of a library called `lgi`.
> Ensure you have the correct lua-lgi package installed.
![Circle clock](https://f.jstanger.dev/github/ironbar/cairo-clock.png)
## Configuration
> Type: `cairo`
| Name | Type | Default | Description |
|--------------------|-----------|---------|----------------------------------------------------|
| `path` | `string` | `null` | The path to the Lua script to load. |
| `frequency` | `float` | `200` | The number of milliseconds between each draw call. |
| `width` | `integer` | `42` | The canvas width in pixels. |
| `height` | `integer` | `42` | The canvas height in pixels. |
<details>
<summary>JSON</summary>
```json
{
"center": [
{
"type": "cairo",
"path": ".config/ironbar/clock.lua",
"frequency": 100,
"width": 300,
"height": 300
}
]
}
```
</details>
<details>
<summary>TOML</summary>
```toml
[[center]]
type = "cairo"
path = ".config/ironbar/clock.lua"
frequency = 100
width = 300
height = 300
```
</details>
<details>
<summary>YAML</summary>
```yaml
center:
- type: cairo
path: .config/ironbar/clock.lua
frequency: 100
width: 300
height: 300
```
</details>
<details>
<summary>Corn</summary>
```corn
let {
$config_dir = ".config/ironbar"
$cairo = {
type = "cairo"
path = "$config_dir/clock.lua"
frequency = 100
width = 300
height = 300
}
} in {
center = [ $cairo ]
}
```
</details>
### Script
Every script must contain a function called `draw`.
This takes a single parameter, which is the Cairo context.
Outside of this, you can do whatever you like.
The full lua `stdlib` is available, and you can load in additional system packages as desired.
The most basic example, which draws a red square, can be seen below:
```lua
function draw(cr)
cr:set_source_rgb(1.0, 0.0, 0.0)
cr:paint()
end
```
A longer example, used to create the clock in the image at the top of the page, is shown below:
<details>
<summary>Circle clock</summary>
```lua
function get_ms()
local ms = tostring(io.popen('date +%s%3N'):read('a')):sub(-4, 9999)
return tonumber(ms) / 1000
end
function draw(cr)
local center_x = 150
local center_y = 150
local radius = 130
local date_table = os.date("*t")
local hours = date_table["hour"]
local minutes = date_table["min"]
local seconds = date_table["sec"]
local ms = get_ms()
local label_seconds = seconds
seconds = seconds + ms
local hours_str = tostring(hours)
if string.len(hours_str) == 1 then
hours_str = "0" .. hours_str
end
local minutes_str = tostring(minutes)
if string.len(minutes_str) == 1 then
minutes_str = "0" .. minutes_str
end
local seconds_str = tostring(label_seconds)
if string.len(seconds_str) == 1 then
seconds_str = "0" .. seconds_str
end
local font_size = radius / 5.5
cr:set_source_rgb(1.0, 1.0, 1.0)
cr:move_to(center_x - font_size * 2.5 + 10, center_y + font_size / 2.5)
cr:set_font_size(font_size)
cr:show_text(hours_str .. ':' .. minutes_str .. ':' .. seconds_str)
cr:stroke()
if hours > 12 then
hours = hours - 12
end
local line_width = radius / 8
local start_angle = -math.pi / 2
local end_angle = start_angle + ((hours + minutes / 60 + seconds / 3600) / 12) * 2 * math.pi
cr:set_line_width(line_width)
cr:arc(center_x, center_y, radius, start_angle, end_angle)
cr:stroke()
end_angle = start_angle + ((minutes + seconds / 60) / 60) * 2 * math.pi
cr:set_line_width(line_width)
cr:arc(center_x, center_y, radius * 0.8, start_angle, end_angle)
cr:stroke()
if seconds == 0 then
seconds = 60
end
end_angle = start_angle + (seconds / 60) * 2 * math.pi
cr:set_line_width(line_width)
cr:arc(center_x, center_y, radius * 0.6, start_angle, end_angle)
cr:stroke()
return 0
end
```
</details>
> [!TIP]
> The C documentation for the Cairo context interface can be found [here](https://www.cairographics.org/manual/cairo-cairo-t.html).
> The Lua interface provides a slightly friendlier API which restructures things slightly.
> The `cairo_` prefix is dropped, and the `cairo_t *cr` parameters are replaced with a namespaced call.
> For example, `cairo_paint (cairo_t *cr)` becomes `cr:paint()`
> [!TIP]
> Ironbar's Cairo module has similar functionality to the popular Conky program.
> You can often re-use scripts with little work.
### Initialization
You can optionally create an `init.lua` file in your config directory.
Any code in here will be executed once, on bar startup.
As variables and functions are global by default in Lua,
this provides a mechanism for sharing code between multiple modules.
## Styling
| Selector | Description |
|----------|-------------------------|
| `.cairo` | Cairo widget container. |
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -13,6 +13,7 @@ Clicking on the widget opens a popup with the time and a calendar.
| `format` | `string` | `%d/%m/%Y %H:%M` | Date/time format string. Pango markup is supported. |
| `format_popup` | `string` | `%H:%M:%S` | Date/time format string to display in the popup header. Pango markup is supported. |
| `locale` | `string` | `$LC_TIME` or `$LANG` or `'POSIX'` | Locale to use (eg `en_GB`). Defaults to the system language (reading from env var). |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the time on the clock button. |
> Detail on available tokens can be found here: <https://docs.rs/chrono/latest/chrono/format/strftime/index.html>

View file

@ -1,6 +1,11 @@
Allows you to compose custom modules consisting of multiple widgets, including popups.
Allows you to compose custom modules consisting of multiple modules and widgets, including popups.
Labels can display dynamic content from scripts, and buttons can interact with the bar or execute commands on click.
The module provides a set of utility widgets, such as containers, labels and buttons.
In addition to these, you can also add any native module.
Paired with the other custom modules such as Cairo,
this provides a powerful declarative interface for constructing your own interfaces.
If you only intend to run a single script, prefer the [script](script) module,
or [label](label) if you only need a single text label.
@ -13,6 +18,11 @@ or [label](label) if you only need a single text label.
This module can be quite fiddly to configure as you effectively have to build a tree of widgets by hand.
It is well worth looking at the examples.
| Name | Type | Default | Description |
|---------|------------------------|------------|------------------------------------------|
| `bar` | `(Module or Widget)[]` | `[]` | Modules and widgets to add to the bar. |
| `popup` | `(Module or Widget)[]` | `null` | Modules and widgets to add to the popup. |
### `Widget`
There are many widget types, each with their own config options.
@ -36,7 +46,7 @@ A container to place nested widgets inside.
| Name | Type | Default | Description |
|---------------|------------------------------------------------------------|----------------|-------------------------------------------------------------------|
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Whether child widgets should be horizontally or vertically added. |
| `widgets` | `Widget[]` | `[]` | List of widgets to add to this box. |
| `widgets` | `(Module or Widget)[]` | `[]` | List of modules/widgets to add to this box. |
#### Label
@ -47,6 +57,7 @@ A text label. Pango markup is supported.
| Name | Type | Default | Description |
|---------|-------------------------------------------------|---------|---------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the label. |
#### Button
@ -54,10 +65,12 @@ A clickable button, which can run a command when clicked.
> Type `button`
| Name | Type | Default | Description |
|------------|-------------------------------------------------|---------|---------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. |
| `on_click` | `string [command]` | `null` | Command to execute. More on this [below](#commands). |
| Name | Type | Default | Description |
|------------|-------------------------------------------------|---------|--------------------------------------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. Ignored if `widgets` is set. |
| `widgets` | `(Module or Widget)[]` | `[]` | List of modules/widgets to add to this button. |
| `on_click` | `string [command]` | `null` | Command to execute. More on this [below](#commands). |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the button. |
#### Image
@ -197,6 +210,7 @@ to help get your head around what's going on:
<button class="power-btn" label="" on_click="!reboot" />
</box>
<label name="uptime" label="Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}" />
<clock disable_popup="true" />
</box>
</popup>
</custom>
@ -252,6 +266,10 @@ to help get your head around what's going on:
"label": "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}",
"name": "uptime",
"type": "label"
},
{
"type": "clock",
"disable_popup": true
}
]
}
@ -309,6 +327,10 @@ type = 'button'
label = '''Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}'''
name = 'uptime'
type = 'label'
[[end.popup.widgets]]
type = 'clock'
disable_popup = true
```
</details>
@ -345,6 +367,8 @@ end:
- label: 'Uptime: {{30000:uptime -p | cut -d '' '' -f2-}}'
name: uptime
type: label
- type: clock
disable_popup: true
type: custom
```
@ -370,6 +394,7 @@ let {
]
}
{ type = "label" name = "uptime" label = "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}" }
{ type = "clock" disable_popup = true }
]
}

View file

@ -19,6 +19,8 @@ Pango markup is supported.
| `interval.temps` | `integer` | `5` | Seconds between refreshing temperature data |
| `interval.disks` | `integer` | `5` | Seconds between refreshing disk data |
| `interval.network` | `integer` | `5` | Seconds between refreshing network data |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the labels. |
| `direction` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | How the labels are laid out (not the rotation of an individual label). |
<details>
<summary>JSON</summary>