mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-21 19:53:05 +00:00
refactor: refactor styling options (#1499)
Introduce a new configuration system for styling.
This commit is contained in:
parent
e4eb69dbd5
commit
28972a1e64
57 changed files with 1763 additions and 1169 deletions
|
@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- `expanded_on_startup` is now `expanded`.
|
||||
- `left_legend` is now `cpu_left_legend`.
|
||||
- [#1472](https://github.com/ClementTsang/bottom/pull/1472): The following arguments have changed names:
|
||||
- `mem_as_value` is now `process_memory_as_value`.
|
||||
- `--mem_as_value` is now `process_memory_as_value`.
|
||||
- [#1472](https://github.com/ClementTsang/bottom/pull/1472): The following config fields have changed names:
|
||||
- `mem_as_value` is now `process_memory_as_value`.
|
||||
- [#1481](https://github.com/ClementTsang/bottom/pull/1481): The following config fields have changed names:
|
||||
|
@ -33,6 +33,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- `mount_filter` is now `disk.mount_filter`.
|
||||
- `temp_filter` is now `temperature.sensor_filter`
|
||||
- `net_filter` is now `network.interface_filter`
|
||||
- [#1499](https://github.com/ClementTsang/bottom/pull/1499): Redesign how styling is configured.
|
||||
- [#1499](https://github.com/ClementTsang/bottom/pull/1499): The following arguments have changed names:
|
||||
- `--colors` is now `--theme`
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ see information on these options by running `btm -h`, or run `btm --help` to dis
|
|||
|
||||
| Option | Behaviour |
|
||||
| ------------------------ | ------------------------------------------ |
|
||||
| `--color <COLOR SCHEME>` | Use a color scheme, use `--help` for info. |
|
||||
| `--theme <COLOR SCHEME>` | Use a color scheme, use `--help` for info. |
|
||||
|
||||
## Other Options
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ each time:
|
|||
| `default_widget_type` | String (one of ["cpu", "proc", "net", "temp", "mem", "disk"], same as layout options) | Sets the default widget type, use --help for more info. |
|
||||
| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. |
|
||||
| `disable_click` | Boolean | Disables mouse clicks. |
|
||||
| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. |
|
||||
| `enable_cache_memory` | Boolean | Enable cache and buffer memory stats (not available on Windows). |
|
||||
| `process_memory_as_value` | Boolean | Defaults to showing process memory usage by value. |
|
||||
| `tree` | Boolean | Defaults to showing the process widget in tree mode. |
|
||||
|
|
159
docs/content/configuration/config-file/styling.md
Normal file
159
docs/content/configuration/config-file/styling.md
Normal file
|
@ -0,0 +1,159 @@
|
|||
# Styling
|
||||
|
||||
Various parts of the bottom can be styled, using either built-in themes or custom theming.
|
||||
|
||||
## Precedence
|
||||
|
||||
As there are a few ways styles can be applied to bottom, the order of which styles are prioritized are, in order of
|
||||
highest precedence to lowest precedence:
|
||||
|
||||
1. Built-in themes set via command-line args (e.g. `btm --theme gruvbox`)
|
||||
2. Custom themes set via config file
|
||||
3. Built-in themes set via config file
|
||||
|
||||
If nothing is set, it will fall back to the default theme.
|
||||
|
||||
## Built-in styles
|
||||
|
||||
bottom has a few built-in themes:
|
||||
|
||||
- Default
|
||||
- [Nord](https://www.nordtheme.com/)
|
||||
- [Gruvbox](https://github.com/morhetz/gruvbox)
|
||||
|
||||
These themes all also have light variants to support terminals using lighter colours.
|
||||
|
||||
To set the theme from the command line:
|
||||
|
||||
```bash
|
||||
btm --theme gruvbox
|
||||
```
|
||||
|
||||
To set the theme using the config file:
|
||||
|
||||
```toml
|
||||
[styles]
|
||||
theme = "gruvbox"
|
||||
```
|
||||
|
||||
## Custom styling
|
||||
|
||||
bottom's components can also be individually styled by the user to control the colour of the text style.
|
||||
|
||||
### Colours
|
||||
|
||||
You can configure the colours for components with strings that are either hex colours (e.g. `"#ffffff"`), RGB colours
|
||||
(e.g. `"255, 255, 255"`), or named colours. Named colours are one of the following strings:
|
||||
|
||||
- `"Black"`
|
||||
- `"Red"`
|
||||
- `"Green"`
|
||||
- `"Yellow"`
|
||||
- `"Blue"`
|
||||
- `"Magenta"`
|
||||
- `"Cyan"`
|
||||
- `"Gray"`
|
||||
- `"DarkGray"`
|
||||
- `"LightRed"`
|
||||
- `"LightGreen"`
|
||||
- `"LightYellow"`
|
||||
- `"LightBlue"`
|
||||
- `"LightMagenta"`
|
||||
- `"LightCyan"`
|
||||
- `"White"`
|
||||
|
||||
### Text
|
||||
|
||||
Text can generally be styled using the following TOML table:
|
||||
|
||||
```toml
|
||||
[field]
|
||||
# The foreground colour of text.
|
||||
color = "black"
|
||||
|
||||
# The background colour of text.
|
||||
bg_color = "blue"
|
||||
|
||||
# Whether to make the text bold.
|
||||
bold = false
|
||||
|
||||
# Inline table version
|
||||
field = { color = "black", bg_color = "blue", bold = false }
|
||||
```
|
||||
|
||||
All fields are optional; by default if `bg_color` is not set then there will be no background color.
|
||||
|
||||
### Configuration
|
||||
|
||||
#### CPU
|
||||
|
||||
These can be set under `[styles.cpu]`:
|
||||
|
||||
| Config field | Details | Examples |
|
||||
| ----------------- | ---------------------------------------------------------------- | -------------------------------------------- |
|
||||
| `all_entry_color` | The colour of the "All" CPU label | `all_entry_color = "Red"` |
|
||||
| `avg_entry_color` | The colour of the average CPU label and graph line | `avg_entry_color = "255, 0, 255"` |
|
||||
| `cpu_core_colors` | Colour of each CPU threads' label and graph line. Read in order. | `cpu_core_colors = ["Red", "Blue", "Green"]` |
|
||||
|
||||
#### Memory
|
||||
|
||||
These can be set under `[styles.memory]`:
|
||||
|
||||
| Config field | Details | Examples |
|
||||
| ------------ | ------------------------------------------------------------------------------ | --------------------------------- |
|
||||
| `ram` | The colour of the RAM label and graph line | `ram = "Red"` |
|
||||
| `cache` | The colour of the cache label and graph line. Does not do anything on Windows. | `cache = "#ffffff"` |
|
||||
| `swap` | The colour of the swap label and graph line | `swap = "255, 0, 255"` |
|
||||
| `arc` | The colour of the ARC label and graph line | `arc = "Blue"` |
|
||||
| `gpus` | Colour of each GPU's memory label and graph line. Read in order. | `gpus = ["Red", "Blue", "Green"]` |
|
||||
|
||||
#### Network
|
||||
|
||||
These can be set under `[styles.network]`:
|
||||
|
||||
| Config field | Details | Examples |
|
||||
| ------------ | --------------------------------------------------------- | ---------------------- |
|
||||
| `rx` | The colour of the RX (download) label and graph line | `rx = "Red"` |
|
||||
| `tx` | The colour of the TX (upload) label and graph line. | `tx = "#ffffff"` |
|
||||
| `rx_total` | The colour of the total RX (download) label in basic mode | `rx_total = "0, 0, 0"` |
|
||||
| `tx_total` | The colour of the total TX (upload) label in basic mode | `tx_total = "#000"` |
|
||||
|
||||
#### Battery
|
||||
|
||||
These can be set under `[styles.battery]`:
|
||||
|
||||
| Config field | Details | Examples |
|
||||
| ---------------- | ------------------------------------------------------------------------ | ---------------------------- |
|
||||
| `high_battery` | The colour of the battery widget bar when the battery is over 50% | `high_battery = "Red"` |
|
||||
| `medium_battery` | The colour of the battery widget bar when the battery between 10% to 50% | `medium_battery = "#ffffff"` |
|
||||
| `low_battery` | The colour of the battery widget bar when the battery is under 10% | `low_battery = "0, 0, 0"` |
|
||||
|
||||
#### Tables
|
||||
|
||||
These can be set under `[styles.tables]`:
|
||||
|
||||
| Config field | Details | Examples |
|
||||
| ------------ | ------------------------------ | -------------------------------------------------------------- |
|
||||
| `headers` | Text styling for table headers | `headers = { color = "red", bg_color = "black", bold = true }` |
|
||||
|
||||
#### Graphs
|
||||
|
||||
These can be set under `[styles.graphs]`:
|
||||
|
||||
| Config field | Details | Examples |
|
||||
| ------------- | -------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| `graph_color` | The general colour of the parts of the graph | `graph_color = "white"` |
|
||||
| `legend_text` | Text styling for graph's legend text | `legend_text = { color = "black", bg_color = "blue", bold = true }` |
|
||||
|
||||
#### General widget settings
|
||||
|
||||
These can be set under `[styles.widgets]`:
|
||||
|
||||
| Config field | Details | Examples |
|
||||
| ----------------- | ------------------------------------------------------------ | --------------------------------------------------------------------- |
|
||||
| `border` | The colour of the widgets' borders | `border = "white"` |
|
||||
| `selected_border` | The colour of a widget's borders when the widget is selected | `selected_border = "white"` |
|
||||
| `widget_title` | Text styling for a widget's title | `widget_title = { color = "black", bg_color = "blue", bold = true }` |
|
||||
| `text` | Text styling for text in general | `text = { color = "black", bg_color = "blue", bold = true }` |
|
||||
| `selected_text` | Text styling for text when representing something selected | `selected_text = { color = "black", bg_color = "blue", bold = true }` |
|
||||
| `disabled_text` | Text styling for text when representing something disabled | `disabled_text = { color = "black", bg_color = "blue", bold = true }` |
|
|
@ -1,33 +0,0 @@
|
|||
# Theming
|
||||
|
||||
!!! Warning
|
||||
|
||||
This section is in progress, and is just copied from the old documentation.
|
||||
|
||||
The config file can be used to set custom colours for parts of the application under the `[colors]` object. The following labels are customizable with strings that are hex colours, RGB colours, or specific named colours.
|
||||
|
||||
Supported named colours are one of the following strings: `Reset, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Gray, DarkGray, LightRed, LightGreen, LightYellow, LightBlue, LightMagenta, LightCyan, White`.
|
||||
|
||||
| Labels | Details | Example |
|
||||
| ------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| Table header colours | Colour of table headers | `table_header_color="255, 255, 255"` |
|
||||
| CPU colour per core | Colour of each core. Read in order. | `cpu_core_colors=["#ffffff", "white", "255, 255, 255"]` |
|
||||
| Average CPU colour | The average CPU color | `avg_cpu_color="White"` |
|
||||
| All CPUs colour | The colour for the "All" CPU label | `all_cpu_color="White"` |
|
||||
| RAM | The colour RAM will use | `ram_color="#ffffff"` |
|
||||
| SWAP | The colour SWAP will use | `swap_color="#ffffff"` |
|
||||
| RX | The colour rx will use | `rx_color="#ffffff"` |
|
||||
| TX | The colour tx will use | `tx_color="#ffffff"` |
|
||||
| Widget title colour | The colour of the label each widget has | `widget_title_color="#ffffff"` |
|
||||
| Border colour | The colour of the border of unselected widgets | `border_color="#ffffff"` |
|
||||
| Selected border colour | The colour of the border of selected widgets | `highlighted_border_color="#ffffff"` |
|
||||
| Text colour | The colour of most text | `text_color="#ffffff"` |
|
||||
| Graph colour | The colour of the lines and text of the graph | `graph_color="#ffffff"` |
|
||||
| Cursor colour | The cursor's colour | `cursor_color="#ffffff"` |
|
||||
| Selected text colour | The colour of text that is selected | `scroll_entry_text_color="#ffffff"` |
|
||||
| Selected text background colour | The background colour of text that is selected | `scroll_entry_bg_color="#ffffff"` |
|
||||
| High battery level colour | The colour used for a high battery level (100% to 50%) | `high_battery_color="green"` |
|
||||
| Medium battery level colour | The colour used for a medium battery level (50% to 10%) | `medium_battery_color="yellow"` |
|
||||
| Low battery level colour | The colour used for a low battery level (10% to 0%) | `low_battery_color="red"` |
|
||||
| GPU colour per gpu | Colour of each gpu. Read in order. | `gpu_core_colors=["#ffffff", "white", "255, 255, 255"]` |
|
||||
| ARC | The colour ARC will use | `arc_color="#ffffff"` |
|
|
@ -106,23 +106,28 @@ If your configuration files aren't working, here are a few things to try:
|
|||
|
||||
### Check the formatting
|
||||
|
||||
It may be handy to refer to the automatically generated config files or the [sample configuration files](https://github.com/ClementTsang/bottom/tree/main/sample_configs).
|
||||
The config files also follow the [TOML](https://toml.io/en/) format.
|
||||
It may be handy to refer to the automatically generated config files or the
|
||||
[sample configuration files](https://github.com/ClementTsang/bottom/tree/main/sample_configs). The config files also
|
||||
follow the [TOML](https://toml.io/en/) format.
|
||||
|
||||
Also make sure your config options are under the right table - for example, to set your temperature type, you must set it under the `[flags]` table:
|
||||
Also make sure your config options are under the right table - for example, to set your temperature type, you must
|
||||
set it under the `[flags]` table:
|
||||
|
||||
```toml
|
||||
[flags]
|
||||
temperature_type = "f"
|
||||
```
|
||||
|
||||
Meanwhile, if you want to set a custom color scheme, it would be under the `[colors]` table:
|
||||
Meanwhile, if you want to set a custom color scheme, it would be under the `[styles]` table:
|
||||
|
||||
```toml
|
||||
[colors]
|
||||
table_header_color="LightBlue"
|
||||
[styles.tables.headers]
|
||||
color="LightBlue"
|
||||
```
|
||||
|
||||
To help validate your configuration files, there is [JSON Schema](https://json-schema.org/) support if your IDE/editor
|
||||
supports it.
|
||||
|
||||
### Check the configuration file location
|
||||
|
||||
Make sure bottom is reading the right configuration file. By default, bottom looks for config files at these locations:
|
||||
|
|
|
@ -166,7 +166,7 @@ nav:
|
|||
- "Config File":
|
||||
- configuration/config-file/index.md
|
||||
- "Flags": configuration/config-file/flags.md
|
||||
- "Theming": configuration/config-file/theming.md
|
||||
- "Styling": configuration/config-file/styling.md
|
||||
- "Layout": configuration/config-file/layout.md
|
||||
- "Data Filtering": configuration/config-file/data-filtering.md
|
||||
- "Processes": configuration/config-file/processes.md
|
||||
|
|
|
@ -127,43 +127,57 @@
|
|||
|
||||
# These are all the components that support custom theming. Note that colour support
|
||||
# will depend on terminal support.
|
||||
#[colors] # Uncomment if you want to use custom colors
|
||||
# Represents the colour of table headers (processes, CPU, disks, temperature).
|
||||
#table_header_color="LightBlue"
|
||||
# Represents the colour of the label each widget has.
|
||||
#widget_title_color="Gray"
|
||||
# Represents the average CPU color.
|
||||
#avg_cpu_color="Red"
|
||||
# Represents the colour the core will use in the CPU legend and graph.
|
||||
#cpu_core_colors=["LightMagenta", "LightYellow", "LightCyan", "LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"]
|
||||
# Represents the colour RAM will use in the memory legend and graph.
|
||||
#ram_color="LightMagenta"
|
||||
# Represents the colour SWAP will use in the memory legend and graph.
|
||||
#swap_color="LightYellow"
|
||||
# Represents the colour ARC will use in the memory legend and graph.
|
||||
#arc_color="LightCyan"
|
||||
# Represents the colour the GPU will use in the legend and graph.
|
||||
#gpu_core_colors=["LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"]
|
||||
# Represents the colour rx will use in the network legend and graph.
|
||||
#rx_color="LightCyan"
|
||||
# Represents the colour tx will use in the network legend and graph.
|
||||
#tx_color="LightGreen"
|
||||
# Represents the colour of the border of unselected widgets.
|
||||
#border_color="Gray"
|
||||
# Represents the colour of the border of selected widgets.
|
||||
#highlighted_border_color="LightBlue"
|
||||
# Represents the colour of most text.
|
||||
#text_color="Gray"
|
||||
# Represents the colour of text that is selected.
|
||||
#selected_text_color="Black"
|
||||
# Represents the background colour of text that is selected.
|
||||
#selected_bg_color="LightBlue"
|
||||
# Represents the colour of the lines and text of the graph.
|
||||
#graph_color="Gray"
|
||||
# Represents the colours of the battery based on charge
|
||||
#high_battery_color="green"
|
||||
#medium_battery_color="yellow"
|
||||
#low_battery_color="red"
|
||||
#[styles] # Uncomment if you want to use custom styling
|
||||
|
||||
# Built-in themes. Valid values are:
|
||||
# - "default"
|
||||
# - "default-light"
|
||||
# - "gruvbox"
|
||||
# - "gruvbox-light"
|
||||
# - "nord"
|
||||
# - "nord-light".
|
||||
#
|
||||
# This will have the lowest precedence if a custom colour palette is set,
|
||||
# or overriden if the command-line flag for a built-in theme is set.
|
||||
#theme = "default"
|
||||
|
||||
#[styles.cpu]
|
||||
#all_entry_color = "green"
|
||||
#avg_entry_color = "red"
|
||||
#cpu_core_colors = ["light magenta", "light yellow", "light cyan", "light green", "light blue", "cyan", "green", "blue"]
|
||||
|
||||
#[styles.memory]
|
||||
#ram = "light magenta"
|
||||
#cache = "light red"
|
||||
#swap = "light yellow"
|
||||
#arc = "light cyan"
|
||||
#gpus = ["light blue", "light red", "cyan", "green", "blue", "red"]
|
||||
|
||||
#[styles.network]
|
||||
#rx = "light magenta"
|
||||
#tx = "light yellow"
|
||||
#rx_total = "light cyan"
|
||||
#tx_total = "light green"
|
||||
|
||||
#[styles.battery]
|
||||
#high_battery = "green"
|
||||
#medium_battery = "yellow"
|
||||
#low_battery = "red"
|
||||
|
||||
#[styles.tables]
|
||||
#headers = {color = "light blue"}
|
||||
|
||||
#[styles.graphs]
|
||||
#graph_color = "gray"
|
||||
#legend_text = {color = "gray"}
|
||||
|
||||
#[styles.widgets]
|
||||
#border = "gray"
|
||||
#selected_border = "light blue"
|
||||
#widget_title = {color = "gray"}
|
||||
#text = {color = "gray"}
|
||||
#selected_text = {color = "black", bg_color = "light blue"}
|
||||
#disabled_text = {color = "dark gray"}
|
||||
|
||||
# Layout - layouts follow a pattern like this:
|
||||
# [[row]] represents a row in the application.
|
||||
|
|
|
@ -13,3 +13,6 @@ whole_word = false
|
|||
regex = true
|
||||
default_widget_type = "cpu"
|
||||
default_widget_count = 1
|
||||
|
||||
[styles]
|
||||
theme = "gruvbox"
|
||||
|
|
|
@ -5,16 +5,6 @@
|
|||
"description": "https://clementtsang.github.io/bottom/nightly/configuration/config-file",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"colors": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColoursConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cpu": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
@ -74,6 +64,16 @@
|
|||
"$ref": "#/definitions/row"
|
||||
}
|
||||
},
|
||||
"styles": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/StyleConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"temperature": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
@ -86,162 +86,45 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"ColoursConfig": {
|
||||
"description": "Colour configuration.",
|
||||
"BatteryStyle": {
|
||||
"description": "Styling specific to the battery widget.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"all_cpu_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"high_battery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"arc_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"low_battery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"avg_cpu_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"border_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"cache_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"cpu_core_colors": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disabled_text_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"gpu_core_colors": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"graph_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"high_battery_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"highlighted_border_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"low_battery_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"medium_battery_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"ram_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"rx_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"rx_total_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"selected_bg_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"selected_text_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"swap_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"table_header_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"text_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"tx_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"tx_total_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"widget_title_color": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"medium_battery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ColorStr": {
|
||||
"type": "string"
|
||||
},
|
||||
"CpuConfig": {
|
||||
"description": "CPU column settings.",
|
||||
"type": "object",
|
||||
|
@ -259,6 +142,41 @@
|
|||
"average"
|
||||
]
|
||||
},
|
||||
"CpuStyle": {
|
||||
"description": "Styling specific to the CPU widget.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"all_entry_color": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"avg_entry_color": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cpu_core_colors": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DiskConfig": {
|
||||
"description": "Disk configuration.",
|
||||
"type": "object",
|
||||
|
@ -340,13 +258,6 @@
|
|||
"null"
|
||||
]
|
||||
},
|
||||
"color": {
|
||||
"description": "For built-in colour palettes.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"cpu_left_legend": {
|
||||
"type": [
|
||||
"boolean",
|
||||
|
@ -565,6 +476,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"GraphStyle": {
|
||||
"description": "General styling for graph widgets.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"graph_color": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"legend_text": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TextStyleConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"IgnoreList": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -595,6 +532,61 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"MemoryStyle": {
|
||||
"description": "Styling specific to the memory widget.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"arc": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cache": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gpus": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
}
|
||||
},
|
||||
"ram": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"swap": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"NetworkConfig": {
|
||||
"description": "Network configuration.",
|
||||
"type": "object",
|
||||
|
@ -612,6 +604,54 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"NetworkStyle": {
|
||||
"description": "Styling specific to the network widget.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"rx": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rx_total": {
|
||||
"description": "Set the colour of the \"rx total\" text. This only affects basic mode.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tx": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tx_total": {
|
||||
"description": "Set the colour of the \"tx total\" text. This only affects basic mode.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProcColumn": {
|
||||
"description": "A column in the process widget.",
|
||||
"type": "string",
|
||||
|
@ -696,6 +736,112 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"StyleConfig": {
|
||||
"description": "Style-related configs.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"battery": {
|
||||
"description": "Styling for the battery widget.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BatteryStyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cpu": {
|
||||
"description": "Styling for the CPU widget.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/CpuStyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"graphs": {
|
||||
"description": "Styling for graph widgets.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/GraphStyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"memory": {
|
||||
"description": "Styling for the memory widget.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/MemoryStyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"network": {
|
||||
"description": "Styling for the network widget.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/NetworkStyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tables": {
|
||||
"description": "Styling for table widgets.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TableStyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"theme": {
|
||||
"description": "A built-in theme.\n\nIf this is and a custom colour are both set, in the config file, the custom colour scheme will be prioritized first. If a theme is set in the command-line args, however, it will always be prioritized first.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"widgets": {
|
||||
"description": "Styling for general widgets.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WidgetStyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"TableStyle": {
|
||||
"description": "General styling for table widgets.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"headers": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TextStyleConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"TempConfig": {
|
||||
"description": "Temperature configuration.",
|
||||
"type": "object",
|
||||
|
@ -713,6 +859,107 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"TextStyleConfig": {
|
||||
"description": "A style for text.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bg_color": {
|
||||
"description": "A built-in ANSI colour, RGB hex, or RGB colour code.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bold": {
|
||||
"description": "Whether to make this text bolded or not. If not set, will default to built-in defaults.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"color": {
|
||||
"description": "A built-in ANSI colour, RGB hex, or RGB colour code.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"WidgetStyle": {
|
||||
"description": "General styling for generic widgets.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"border": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"disabled_text": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TextStyleConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"selected_border": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ColorStr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"selected_text": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TextStyleConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"text": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TextStyleConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"widget_title": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TextStyleConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"row": {
|
||||
"description": "Represents a row. This has a length of some sort (optional) and a vector of children.",
|
||||
"type": "object",
|
||||
|
|
16
src/app.rs
16
src/app.rs
|
@ -1529,16 +1529,14 @@ impl App {
|
|||
}
|
||||
|
||||
fn move_widget_selection_logic(&mut self, direction: &WidgetDirection) {
|
||||
/*
|
||||
The actual logic for widget movement.
|
||||
// The actual logic for widget movement.
|
||||
|
||||
We follow these following steps:
|
||||
1. Send a movement signal in `direction`.
|
||||
2. Check if this new widget we've landed on is hidden. If not, halt.
|
||||
3. If it hidden, loop and either send:
|
||||
- A signal equal to the current direction, if it is opposite of the reflection.
|
||||
- Reflection direction.
|
||||
*/
|
||||
// We follow these following steps:
|
||||
// 1. Send a movement signal in `direction`.
|
||||
// 2. Check if this new widget we've landed on is hidden. If not, halt.
|
||||
// 3. If it hidden, loop and either send:
|
||||
// - A signal equal to the current direction, if it is opposite of the reflection.
|
||||
// - Reflection direction.
|
||||
|
||||
if !self.ignore_normal_keybinds() && !self.is_expanded {
|
||||
if let Some(new_widget_id) = &(match direction {
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
pub mod components;
|
||||
mod dialogs;
|
||||
mod drawing_utils;
|
||||
pub mod styling;
|
||||
mod widgets;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use itertools::izip;
|
||||
use styling::*;
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
|
@ -22,42 +18,12 @@ use crate::{
|
|||
App,
|
||||
},
|
||||
constants::*,
|
||||
options::OptionError,
|
||||
options::config::style::ColourPalette,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ColourScheme {
|
||||
Default,
|
||||
DefaultLight,
|
||||
Gruvbox,
|
||||
GruvboxLight,
|
||||
Nord,
|
||||
NordLight,
|
||||
Custom,
|
||||
}
|
||||
|
||||
impl FromStr for ColourScheme {
|
||||
type Err = OptionError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let lower_case = s.to_lowercase();
|
||||
match lower_case.as_str() {
|
||||
"default" => Ok(ColourScheme::Default),
|
||||
"default-light" => Ok(ColourScheme::DefaultLight),
|
||||
"gruvbox" => Ok(ColourScheme::Gruvbox),
|
||||
"gruvbox-light" => Ok(ColourScheme::GruvboxLight),
|
||||
"nord" => Ok(ColourScheme::Nord),
|
||||
"nord-light" => Ok(ColourScheme::NordLight),
|
||||
_ => Err(OptionError::other(format!(
|
||||
"'{s}' is an invalid built-in color scheme."
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the canvas' state.
|
||||
pub struct Painter {
|
||||
pub colours: CanvasStyling,
|
||||
pub colours: ColourPalette,
|
||||
previous_height: u16,
|
||||
previous_width: u16,
|
||||
|
||||
|
@ -81,7 +47,7 @@ pub enum LayoutConstraint {
|
|||
}
|
||||
|
||||
impl Painter {
|
||||
pub fn init(layout: BottomLayout, styling: CanvasStyling) -> anyhow::Result<Self> {
|
||||
pub fn init(layout: BottomLayout, styling: ColourPalette) -> anyhow::Result<Self> {
|
||||
// Now for modularity; we have to also initialize the base layouts!
|
||||
// We want to do this ONCE and reuse; after this we can just construct
|
||||
// based on the console size.
|
||||
|
@ -193,7 +159,7 @@ impl Painter {
|
|||
f.render_widget(
|
||||
Paragraph::new(Span::styled(
|
||||
"Frozen, press 'f' to unfreeze",
|
||||
self.colours.currently_selected_text_style,
|
||||
self.colours.selected_text_style,
|
||||
)),
|
||||
Layout::default()
|
||||
.horizontal_margin(1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use tui::style::Style;
|
||||
|
||||
use crate::canvas::styling::CanvasStyling;
|
||||
use crate::options::config::style::ColourPalette;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DataTableStyling {
|
||||
|
@ -13,13 +13,13 @@ pub struct DataTableStyling {
|
|||
}
|
||||
|
||||
impl DataTableStyling {
|
||||
pub fn from_colours(colours: &CanvasStyling) -> Self {
|
||||
pub fn from_palette(colours: &ColourPalette) -> Self {
|
||||
Self {
|
||||
header_style: colours.table_header_style,
|
||||
border_style: colours.border_style,
|
||||
highlighted_border_style: colours.highlighted_border_style,
|
||||
text_style: colours.text_style,
|
||||
highlighted_text_style: colours.currently_selected_text_style,
|
||||
highlighted_text_style: colours.selected_text_style,
|
||||
title_style: colours.widget_title_style,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,12 +211,12 @@ impl Painter {
|
|||
if MAX_PROCESS_SIGNAL == 1 || !app_state.app_config_fields.is_advanced_kill {
|
||||
let (yes_button, no_button) = match app_state.delete_dialog_state.selected_signal {
|
||||
KillSignal::Kill(_) => (
|
||||
Span::styled("Yes", self.colours.currently_selected_text_style),
|
||||
Span::styled("Yes", self.colours.selected_text_style),
|
||||
Span::styled("No", self.colours.text_style),
|
||||
),
|
||||
KillSignal::Cancel => (
|
||||
Span::styled("Yes", self.colours.text_style),
|
||||
Span::styled("No", self.colours.currently_selected_text_style),
|
||||
Span::styled("No", self.colours.selected_text_style),
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -325,10 +325,8 @@ impl Painter {
|
|||
.map(|text| Span::styled(*text, self.colours.text_style))
|
||||
.collect::<Vec<Span<'_>>>();
|
||||
buttons.insert(0, Span::styled(SIGNAL_TEXT[0], self.colours.text_style));
|
||||
buttons[selected - scroll_offset] = Span::styled(
|
||||
SIGNAL_TEXT[selected],
|
||||
self.colours.currently_selected_text_style,
|
||||
);
|
||||
buttons[selected - scroll_offset] =
|
||||
Span::styled(SIGNAL_TEXT[selected], self.colours.selected_text_style);
|
||||
|
||||
app_state.delete_dialog_state.button_positions = layout
|
||||
.iter()
|
||||
|
|
|
@ -1,305 +0,0 @@
|
|||
mod colour_utils;
|
||||
|
||||
use colour_utils::*;
|
||||
use tui::style::{Color, Style};
|
||||
|
||||
use super::ColourScheme;
|
||||
pub use crate::options::ConfigV1;
|
||||
use crate::{
|
||||
constants::*,
|
||||
options::{colours::ColoursConfig, OptionError, OptionResult},
|
||||
};
|
||||
|
||||
pub struct CanvasStyling {
|
||||
pub currently_selected_text_colour: Color,
|
||||
pub currently_selected_bg_colour: Color,
|
||||
pub currently_selected_text_style: Style,
|
||||
pub table_header_style: Style,
|
||||
pub ram_style: Style,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub cache_style: Style,
|
||||
pub swap_style: Style,
|
||||
pub arc_style: Style,
|
||||
pub gpu_colour_styles: Vec<Style>,
|
||||
pub rx_style: Style,
|
||||
pub tx_style: Style,
|
||||
pub total_rx_style: Style,
|
||||
pub total_tx_style: Style,
|
||||
pub all_colour_style: Style,
|
||||
pub avg_colour_style: Style,
|
||||
pub cpu_colour_styles: Vec<Style>,
|
||||
pub border_style: Style,
|
||||
pub highlighted_border_style: Style,
|
||||
pub text_style: Style,
|
||||
pub widget_title_style: Style,
|
||||
pub graph_style: Style,
|
||||
pub high_battery_colour: Style,
|
||||
pub medium_battery_colour: Style,
|
||||
pub low_battery_colour: Style,
|
||||
pub invalid_query_style: Style,
|
||||
pub disabled_text_style: Style,
|
||||
}
|
||||
|
||||
impl Default for CanvasStyling {
|
||||
fn default() -> Self {
|
||||
let text_colour = Color::Gray;
|
||||
let currently_selected_text_colour = Color::Black;
|
||||
let currently_selected_bg_colour = HIGHLIGHT_COLOUR;
|
||||
|
||||
CanvasStyling {
|
||||
currently_selected_text_colour,
|
||||
currently_selected_bg_colour,
|
||||
currently_selected_text_style: Style::default()
|
||||
.fg(currently_selected_text_colour)
|
||||
.bg(currently_selected_bg_colour),
|
||||
table_header_style: Style::default().fg(HIGHLIGHT_COLOUR),
|
||||
ram_style: Style::default().fg(FIRST_COLOUR),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: Style::default().fg(FIFTH_COLOUR),
|
||||
swap_style: Style::default().fg(SECOND_COLOUR),
|
||||
arc_style: Style::default().fg(THIRD_COLOUR),
|
||||
gpu_colour_styles: vec![
|
||||
Style::default().fg(FOURTH_COLOUR),
|
||||
Style::default().fg(Color::LightBlue),
|
||||
Style::default().fg(Color::LightRed),
|
||||
Style::default().fg(Color::Cyan),
|
||||
Style::default().fg(Color::Green),
|
||||
Style::default().fg(Color::Blue),
|
||||
Style::default().fg(Color::Red),
|
||||
],
|
||||
rx_style: Style::default().fg(FIRST_COLOUR),
|
||||
tx_style: Style::default().fg(SECOND_COLOUR),
|
||||
total_rx_style: Style::default().fg(THIRD_COLOUR),
|
||||
total_tx_style: Style::default().fg(FOURTH_COLOUR),
|
||||
all_colour_style: Style::default().fg(ALL_COLOUR),
|
||||
avg_colour_style: Style::default().fg(AVG_COLOUR),
|
||||
cpu_colour_styles: vec![
|
||||
Style::default().fg(Color::LightMagenta),
|
||||
Style::default().fg(Color::LightYellow),
|
||||
Style::default().fg(Color::LightCyan),
|
||||
Style::default().fg(Color::LightGreen),
|
||||
Style::default().fg(Color::LightBlue),
|
||||
Style::default().fg(Color::Cyan),
|
||||
Style::default().fg(Color::Green),
|
||||
Style::default().fg(Color::Blue),
|
||||
],
|
||||
border_style: Style::default().fg(text_colour),
|
||||
highlighted_border_style: Style::default().fg(HIGHLIGHT_COLOUR),
|
||||
text_style: Style::default().fg(text_colour),
|
||||
widget_title_style: Style::default().fg(text_colour),
|
||||
graph_style: Style::default().fg(text_colour),
|
||||
high_battery_colour: Style::default().fg(Color::Green),
|
||||
medium_battery_colour: Style::default().fg(Color::Yellow),
|
||||
low_battery_colour: Style::default().fg(Color::Red),
|
||||
invalid_query_style: Style::default().fg(Color::Red),
|
||||
disabled_text_style: Style::default().fg(Color::DarkGray),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! try_set_colour {
|
||||
($field:expr, $colours:expr, $colour_field:ident) => {
|
||||
if let Some(colour_str) = &$colours.$colour_field {
|
||||
$field = str_to_fg(colour_str).map_err(|err| {
|
||||
OptionError::config(format!(
|
||||
"Please update 'colors.{}' in your config file. {err}",
|
||||
stringify!($colour_field)
|
||||
))
|
||||
})?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! try_set_colour_list {
|
||||
($field:expr, $colours:expr, $colour_field:ident) => {
|
||||
if let Some(colour_list) = &$colours.$colour_field {
|
||||
$field = colour_list
|
||||
.iter()
|
||||
.map(|s| str_to_fg(s))
|
||||
.collect::<Result<Vec<Style>, String>>()
|
||||
.map_err(|err| {
|
||||
OptionError::config(format!(
|
||||
"Please update 'colors.{}' in your config file. {err}",
|
||||
stringify!($colour_field)
|
||||
))
|
||||
})?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl CanvasStyling {
|
||||
pub fn new(colour_scheme: ColourScheme, config: &ConfigV1) -> anyhow::Result<Self> {
|
||||
let mut canvas_colours = Self::default();
|
||||
|
||||
match colour_scheme {
|
||||
ColourScheme::Default => {}
|
||||
ColourScheme::DefaultLight => {
|
||||
canvas_colours.set_colours_from_palette(&default_light_mode_colour_palette())?;
|
||||
}
|
||||
ColourScheme::Gruvbox => {
|
||||
canvas_colours.set_colours_from_palette(&gruvbox_colour_palette())?;
|
||||
}
|
||||
ColourScheme::GruvboxLight => {
|
||||
canvas_colours.set_colours_from_palette(&gruvbox_light_colour_palette())?;
|
||||
}
|
||||
ColourScheme::Nord => {
|
||||
canvas_colours.set_colours_from_palette(&nord_colour_palette())?;
|
||||
}
|
||||
ColourScheme::NordLight => {
|
||||
canvas_colours.set_colours_from_palette(&nord_light_colour_palette())?;
|
||||
}
|
||||
ColourScheme::Custom => {
|
||||
if let Some(colors) = &config.colors {
|
||||
canvas_colours.set_colours_from_palette(colors)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(canvas_colours)
|
||||
}
|
||||
|
||||
pub fn set_colours_from_palette(&mut self, colours: &ColoursConfig) -> OptionResult<()> {
|
||||
// CPU
|
||||
try_set_colour!(self.avg_colour_style, colours, avg_cpu_color);
|
||||
try_set_colour!(self.all_colour_style, colours, all_cpu_color);
|
||||
try_set_colour_list!(self.cpu_colour_styles, colours, cpu_core_colors);
|
||||
|
||||
// Memory
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
try_set_colour!(self.cache_style, colours, cache_color);
|
||||
|
||||
#[cfg(feature = "zfs")]
|
||||
try_set_colour!(self.arc_style, colours, arc_color);
|
||||
|
||||
#[cfg(feature = "gpu")]
|
||||
try_set_colour_list!(self.gpu_colour_styles, colours, gpu_core_colors);
|
||||
|
||||
try_set_colour!(self.ram_style, colours, ram_color);
|
||||
try_set_colour!(self.swap_style, colours, swap_color);
|
||||
|
||||
// Network
|
||||
try_set_colour!(self.rx_style, colours, rx_color);
|
||||
try_set_colour!(self.tx_style, colours, tx_color);
|
||||
try_set_colour!(self.total_rx_style, colours, rx_total_color);
|
||||
try_set_colour!(self.total_tx_style, colours, tx_total_color);
|
||||
|
||||
// Battery
|
||||
try_set_colour!(self.high_battery_colour, colours, high_battery_color);
|
||||
try_set_colour!(self.medium_battery_colour, colours, medium_battery_color);
|
||||
try_set_colour!(self.low_battery_colour, colours, low_battery_color);
|
||||
|
||||
// Widget text and graphs
|
||||
try_set_colour!(self.widget_title_style, colours, widget_title_color);
|
||||
try_set_colour!(self.graph_style, colours, graph_color);
|
||||
try_set_colour!(self.text_style, colours, text_color);
|
||||
try_set_colour!(self.disabled_text_style, colours, disabled_text_color);
|
||||
try_set_colour!(self.border_style, colours, border_color);
|
||||
try_set_colour!(
|
||||
self.highlighted_border_style,
|
||||
colours,
|
||||
highlighted_border_color
|
||||
);
|
||||
|
||||
// Tables
|
||||
try_set_colour!(self.table_header_style, colours, table_header_color);
|
||||
|
||||
if let Some(scroll_entry_text_color) = &colours.selected_text_color {
|
||||
self.set_scroll_entry_text_color(scroll_entry_text_color)
|
||||
.map_err(|err| {
|
||||
OptionError::config(format!(
|
||||
"Please update 'colors.selected_text_color' in your config file. {err}",
|
||||
))
|
||||
})?
|
||||
}
|
||||
|
||||
if let Some(scroll_entry_bg_color) = &colours.selected_bg_color {
|
||||
self.set_scroll_entry_bg_color(scroll_entry_bg_color)
|
||||
.map_err(|err| {
|
||||
OptionError::config(format!(
|
||||
"Please update 'colors.selected_bg_color' in your config file. {err}",
|
||||
))
|
||||
})?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_scroll_entry_text_color(&mut self, colour: &str) -> Result<(), String> {
|
||||
self.currently_selected_text_colour = str_to_colour(colour)?;
|
||||
self.currently_selected_text_style = Style::default()
|
||||
.fg(self.currently_selected_text_colour)
|
||||
.bg(self.currently_selected_bg_colour);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_scroll_entry_bg_color(&mut self, colour: &str) -> Result<(), String> {
|
||||
self.currently_selected_bg_colour = str_to_colour(colour)?;
|
||||
self.currently_selected_text_style = Style::default()
|
||||
.fg(self.currently_selected_text_colour)
|
||||
.bg(self.currently_selected_bg_colour);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use tui::style::{Color, Style};
|
||||
|
||||
use super::{CanvasStyling, ColourScheme};
|
||||
use crate::options::ConfigV1;
|
||||
|
||||
#[test]
|
||||
fn default_selected_colour_works() {
|
||||
let mut colours = CanvasStyling::default();
|
||||
|
||||
assert_eq!(
|
||||
colours.currently_selected_text_style,
|
||||
Style::default()
|
||||
.fg(colours.currently_selected_text_colour)
|
||||
.bg(colours.currently_selected_bg_colour),
|
||||
);
|
||||
|
||||
colours.set_scroll_entry_text_color("red").unwrap();
|
||||
assert_eq!(
|
||||
colours.currently_selected_text_style,
|
||||
Style::default()
|
||||
.fg(Color::Red)
|
||||
.bg(colours.currently_selected_bg_colour),
|
||||
);
|
||||
|
||||
colours.set_scroll_entry_bg_color("magenta").unwrap();
|
||||
assert_eq!(
|
||||
colours.currently_selected_text_style,
|
||||
Style::default().fg(Color::Red).bg(Color::Magenta),
|
||||
);
|
||||
|
||||
colours.set_scroll_entry_text_color("fake red").unwrap_err();
|
||||
assert_eq!(
|
||||
colours.currently_selected_text_style,
|
||||
Style::default()
|
||||
.fg(Color::Red)
|
||||
.bg(colours.currently_selected_bg_colour),
|
||||
);
|
||||
|
||||
colours
|
||||
.set_scroll_entry_bg_color("fake magenta")
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
colours.currently_selected_text_style,
|
||||
Style::default().fg(Color::Red).bg(Color::Magenta),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn built_in_colour_schemes_work() {
|
||||
let config = ConfigV1::default();
|
||||
CanvasStyling::new(ColourScheme::Default, &config).unwrap();
|
||||
CanvasStyling::new(ColourScheme::DefaultLight, &config).unwrap();
|
||||
CanvasStyling::new(ColourScheme::Gruvbox, &config).unwrap();
|
||||
CanvasStyling::new(ColourScheme::GruvboxLight, &config).unwrap();
|
||||
CanvasStyling::new(ColourScheme::Nord, &config).unwrap();
|
||||
CanvasStyling::new(ColourScheme::NordLight, &config).unwrap();
|
||||
}
|
||||
}
|
|
@ -96,7 +96,7 @@ impl Painter {
|
|||
)
|
||||
.divider(tui::symbols::line::VERTICAL)
|
||||
.style(self.colours.text_style)
|
||||
.highlight_style(self.colours.currently_selected_text_style)
|
||||
.highlight_style(self.colours.selected_text_style)
|
||||
.select(battery_widget_state.currently_selected_battery_index),
|
||||
tab_draw_loc,
|
||||
);
|
||||
|
@ -148,11 +148,11 @@ impl Painter {
|
|||
]));
|
||||
battery_charge_rows.push(Row::new([Cell::from(bars).style(
|
||||
if charge_percentage < 10.0 {
|
||||
self.colours.low_battery_colour
|
||||
self.colours.low_battery
|
||||
} else if charge_percentage < 50.0 {
|
||||
self.colours.medium_battery_colour
|
||||
self.colours.medium_battery
|
||||
} else {
|
||||
self.colours.high_battery_colour
|
||||
self.colours.high_battery
|
||||
},
|
||||
)]));
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ impl Painter {
|
|||
last_entry,
|
||||
} => {
|
||||
let (outer, style) = match data_type {
|
||||
CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_colour_style),
|
||||
CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_cpu_colour),
|
||||
CpuDataType::Cpu(index) => (
|
||||
format!("{index:<3}",),
|
||||
self.colours.cpu_colour_styles
|
||||
|
|
|
@ -136,9 +136,9 @@ impl Painter {
|
|||
CpuWidgetData::All => None,
|
||||
CpuWidgetData::Entry { data, .. } => {
|
||||
let style = if show_avg_cpu && itx == AVG_POSITION {
|
||||
self.colours.avg_colour_style
|
||||
self.colours.avg_cpu_colour
|
||||
} else if itx == ALL_POSITION {
|
||||
self.colours.all_colour_style
|
||||
self.colours.all_cpu_colour
|
||||
} else {
|
||||
let offset_position = itx - 1; // Because of the all position
|
||||
self.colours.cpu_colour_styles[(offset_position - show_avg_offset)
|
||||
|
@ -158,7 +158,7 @@ impl Painter {
|
|||
cpu_data.get(current_scroll_position)
|
||||
{
|
||||
let style = if show_avg_cpu && current_scroll_position == AVG_POSITION {
|
||||
self.colours.avg_colour_style
|
||||
self.colours.avg_cpu_colour
|
||||
} else {
|
||||
let offset_position = current_scroll_position - 1; // Because of the all position
|
||||
self.colours.cpu_colour_styles
|
||||
|
|
|
@ -133,7 +133,7 @@ impl Painter {
|
|||
#[cfg(feature = "gpu")]
|
||||
{
|
||||
if let Some(gpu_data) = &app_state.converted_data.gpu_data {
|
||||
let gpu_styles = &self.colours.gpu_colour_styles;
|
||||
let gpu_styles = &self.colours.gpu_colours;
|
||||
let mut color_index = 0;
|
||||
|
||||
gpu_data.iter().for_each(|gpu_data_vec| {
|
||||
|
|
|
@ -89,7 +89,7 @@ impl Painter {
|
|||
{
|
||||
if let Some(gpu_data) = &app_state.converted_data.gpu_data {
|
||||
let mut color_index = 0;
|
||||
let gpu_styles = &self.colours.gpu_colour_styles;
|
||||
let gpu_styles = &self.colours.gpu_colours;
|
||||
gpu_data.iter().for_each(|gpu| {
|
||||
let gpu_label =
|
||||
format!("{}:{}{}", gpu.name, gpu.mem_percent, gpu.mem_total);
|
||||
|
|
|
@ -183,7 +183,7 @@ impl Painter {
|
|||
&proc_widget_state.proc_search.search_state,
|
||||
available_width,
|
||||
is_on_widget,
|
||||
self.colours.currently_selected_text_style,
|
||||
self.colours.selected_text_style,
|
||||
self.colours.text_style,
|
||||
);
|
||||
|
||||
|
@ -203,19 +203,19 @@ impl Painter {
|
|||
|
||||
// Text options shamelessly stolen from VS Code.
|
||||
let case_style = if !proc_widget_state.proc_search.is_ignoring_case {
|
||||
self.colours.currently_selected_text_style
|
||||
self.colours.selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let whole_word_style = if proc_widget_state.proc_search.is_searching_whole_word {
|
||||
self.colours.currently_selected_text_style
|
||||
self.colours.selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let regex_style = if proc_widget_state.proc_search.is_searching_with_regex {
|
||||
self.colours.currently_selected_text_style
|
||||
self.colours.selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
};
|
||||
|
|
346
src/constants.rs
346
src/constants.rs
|
@ -1,7 +1,5 @@
|
|||
use tui::widgets::Borders;
|
||||
|
||||
use crate::options::ColoursConfig;
|
||||
|
||||
// Default widget ID
|
||||
pub const DEFAULT_WIDGET_ID: u64 = 56709;
|
||||
|
||||
|
@ -24,260 +22,6 @@ pub const TIME_LABEL_HEIGHT_LIMIT: u16 = 7;
|
|||
// Side borders
|
||||
pub const SIDE_BORDERS: Borders = Borders::LEFT.union(Borders::RIGHT);
|
||||
|
||||
// Colour profiles
|
||||
// TODO: Generate these with a macro or something...
|
||||
pub fn default_light_mode_colour_palette() -> ColoursConfig {
|
||||
ColoursConfig {
|
||||
text_color: Some("black".into()),
|
||||
border_color: Some("black".into()),
|
||||
table_header_color: Some("black".into()),
|
||||
widget_title_color: Some("black".into()),
|
||||
selected_text_color: Some("white".into()),
|
||||
graph_color: Some("black".into()),
|
||||
disabled_text_color: Some("gray".into()),
|
||||
ram_color: Some("blue".into()),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_color: Some("LightRed".into()),
|
||||
swap_color: Some("red".into()),
|
||||
arc_color: Some("LightBlue".into()),
|
||||
gpu_core_colors: Some(vec![
|
||||
"LightGreen".into(),
|
||||
"LightCyan".into(),
|
||||
"LightRed".into(),
|
||||
"Cyan".into(),
|
||||
"Green".into(),
|
||||
"Blue".into(),
|
||||
"Red".into(),
|
||||
]),
|
||||
rx_color: Some("blue".into()),
|
||||
tx_color: Some("red".into()),
|
||||
rx_total_color: Some("LightBlue".into()),
|
||||
tx_total_color: Some("LightRed".into()),
|
||||
cpu_core_colors: Some(vec![
|
||||
"LightMagenta".into(),
|
||||
"LightBlue".into(),
|
||||
"LightRed".into(),
|
||||
"Cyan".into(),
|
||||
"Green".into(),
|
||||
"Blue".into(),
|
||||
"Red".into(),
|
||||
]),
|
||||
..ColoursConfig::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gruvbox_colour_palette() -> ColoursConfig {
|
||||
ColoursConfig {
|
||||
table_header_color: Some("#83a598".into()),
|
||||
all_cpu_color: Some("#8ec07c".into()),
|
||||
avg_cpu_color: Some("#fb4934".into()),
|
||||
cpu_core_colors: Some(vec![
|
||||
"#cc241d".into(),
|
||||
"#98971a".into(),
|
||||
"#d79921".into(),
|
||||
"#458588".into(),
|
||||
"#b16286".into(),
|
||||
"#689d6a".into(),
|
||||
"#fe8019".into(),
|
||||
"#b8bb26".into(),
|
||||
"#fabd2f".into(),
|
||||
"#83a598".into(),
|
||||
"#d3869b".into(),
|
||||
"#d65d0e".into(),
|
||||
"#9d0006".into(),
|
||||
"#79740e".into(),
|
||||
"#b57614".into(),
|
||||
"#076678".into(),
|
||||
"#8f3f71".into(),
|
||||
"#427b58".into(),
|
||||
"#d65d03".into(),
|
||||
"#af3a03".into(),
|
||||
]),
|
||||
ram_color: Some("#8ec07c".into()),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_color: Some("#b16286".into()),
|
||||
swap_color: Some("#fabd2f".into()),
|
||||
arc_color: Some("#689d6a".into()),
|
||||
gpu_core_colors: Some(vec![
|
||||
"#d79921".into(),
|
||||
"#458588".into(),
|
||||
"#b16286".into(),
|
||||
"#fe8019".into(),
|
||||
"#b8bb26".into(),
|
||||
"#cc241d".into(),
|
||||
"#98971a".into(),
|
||||
]),
|
||||
rx_color: Some("#8ec07c".into()),
|
||||
tx_color: Some("#fabd2f".into()),
|
||||
rx_total_color: Some("#689d6a".into()),
|
||||
tx_total_color: Some("#d79921".into()),
|
||||
border_color: Some("#ebdbb2".into()),
|
||||
highlighted_border_color: Some("#fe8019".into()),
|
||||
disabled_text_color: Some("#665c54".into()),
|
||||
text_color: Some("#ebdbb2".into()),
|
||||
selected_text_color: Some("#1d2021".into()),
|
||||
selected_bg_color: Some("#ebdbb2".into()),
|
||||
widget_title_color: Some("#ebdbb2".into()),
|
||||
graph_color: Some("#ebdbb2".into()),
|
||||
high_battery_color: Some("#98971a".into()),
|
||||
medium_battery_color: Some("#fabd2f".into()),
|
||||
low_battery_color: Some("#fb4934".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gruvbox_light_colour_palette() -> ColoursConfig {
|
||||
ColoursConfig {
|
||||
table_header_color: Some("#076678".into()),
|
||||
all_cpu_color: Some("#8ec07c".into()),
|
||||
avg_cpu_color: Some("#fb4934".into()),
|
||||
cpu_core_colors: Some(vec![
|
||||
"#cc241d".into(),
|
||||
"#98971a".into(),
|
||||
"#d79921".into(),
|
||||
"#458588".into(),
|
||||
"#b16286".into(),
|
||||
"#689d6a".into(),
|
||||
"#fe8019".into(),
|
||||
"#b8bb26".into(),
|
||||
"#fabd2f".into(),
|
||||
"#83a598".into(),
|
||||
"#d3869b".into(),
|
||||
"#d65d0e".into(),
|
||||
"#9d0006".into(),
|
||||
"#79740e".into(),
|
||||
"#b57614".into(),
|
||||
"#076678".into(),
|
||||
"#8f3f71".into(),
|
||||
"#427b58".into(),
|
||||
"#d65d03".into(),
|
||||
"#af3a03".into(),
|
||||
]),
|
||||
ram_color: Some("#427b58".into()),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_color: Some("#d79921".into()),
|
||||
swap_color: Some("#cc241d".into()),
|
||||
arc_color: Some("#689d6a".into()),
|
||||
gpu_core_colors: Some(vec![
|
||||
"#9d0006".into(),
|
||||
"#98971a".into(),
|
||||
"#d79921".into(),
|
||||
"#458588".into(),
|
||||
"#b16286".into(),
|
||||
"#fe8019".into(),
|
||||
"#b8bb26".into(),
|
||||
]),
|
||||
rx_color: Some("#427b58".into()),
|
||||
tx_color: Some("#cc241d".into()),
|
||||
rx_total_color: Some("#689d6a".into()),
|
||||
tx_total_color: Some("#9d0006".into()),
|
||||
border_color: Some("#3c3836".into()),
|
||||
highlighted_border_color: Some("#af3a03".into()),
|
||||
disabled_text_color: Some("#d5c4a1".into()),
|
||||
text_color: Some("#3c3836".into()),
|
||||
selected_text_color: Some("#ebdbb2".into()),
|
||||
selected_bg_color: Some("#3c3836".into()),
|
||||
widget_title_color: Some("#3c3836".into()),
|
||||
graph_color: Some("#3c3836".into()),
|
||||
high_battery_color: Some("#98971a".into()),
|
||||
medium_battery_color: Some("#d79921".into()),
|
||||
low_battery_color: Some("#cc241d".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nord_colour_palette() -> ColoursConfig {
|
||||
ColoursConfig {
|
||||
table_header_color: Some("#81a1c1".into()),
|
||||
all_cpu_color: Some("#88c0d0".into()),
|
||||
avg_cpu_color: Some("#8fbcbb".into()),
|
||||
cpu_core_colors: Some(vec![
|
||||
"#5e81ac".into(),
|
||||
"#81a1c1".into(),
|
||||
"#d8dee9".into(),
|
||||
"#b48ead".into(),
|
||||
"#a3be8c".into(),
|
||||
"#ebcb8b".into(),
|
||||
"#d08770".into(),
|
||||
"#bf616a".into(),
|
||||
]),
|
||||
ram_color: Some("#88c0d0".into()),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_color: Some("#d8dee9".into()),
|
||||
swap_color: Some("#d08770".into()),
|
||||
arc_color: Some("#5e81ac".into()),
|
||||
gpu_core_colors: Some(vec![
|
||||
"#8fbcbb".into(),
|
||||
"#81a1c1".into(),
|
||||
"#d8dee9".into(),
|
||||
"#b48ead".into(),
|
||||
"#a3be8c".into(),
|
||||
"#ebcb8b".into(),
|
||||
"#bf616a".into(),
|
||||
]),
|
||||
rx_color: Some("#88c0d0".into()),
|
||||
tx_color: Some("#d08770".into()),
|
||||
rx_total_color: Some("#5e81ac".into()),
|
||||
tx_total_color: Some("#8fbcbb".into()),
|
||||
border_color: Some("#88c0d0".into()),
|
||||
highlighted_border_color: Some("#5e81ac".into()),
|
||||
disabled_text_color: Some("#4c566a".into()),
|
||||
text_color: Some("#e5e9f0".into()),
|
||||
selected_text_color: Some("#2e3440".into()),
|
||||
selected_bg_color: Some("#88c0d0".into()),
|
||||
widget_title_color: Some("#e5e9f0".into()),
|
||||
graph_color: Some("#e5e9f0".into()),
|
||||
high_battery_color: Some("#a3be8c".into()),
|
||||
medium_battery_color: Some("#ebcb8b".into()),
|
||||
low_battery_color: Some("#bf616a".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nord_light_colour_palette() -> ColoursConfig {
|
||||
ColoursConfig {
|
||||
table_header_color: Some("#5e81ac".into()),
|
||||
all_cpu_color: Some("#81a1c1".into()),
|
||||
avg_cpu_color: Some("#8fbcbb".into()),
|
||||
cpu_core_colors: Some(vec![
|
||||
"#5e81ac".into(),
|
||||
"#88c0d0".into(),
|
||||
"#4c566a".into(),
|
||||
"#b48ead".into(),
|
||||
"#a3be8c".into(),
|
||||
"#ebcb8b".into(),
|
||||
"#d08770".into(),
|
||||
"#bf616a".into(),
|
||||
]),
|
||||
ram_color: Some("#81a1c1".into()),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_color: Some("#4c566a".into()),
|
||||
swap_color: Some("#d08770".into()),
|
||||
arc_color: Some("#5e81ac".into()),
|
||||
gpu_core_colors: Some(vec![
|
||||
"#8fbcbb".into(),
|
||||
"#88c0d0".into(),
|
||||
"#4c566a".into(),
|
||||
"#b48ead".into(),
|
||||
"#a3be8c".into(),
|
||||
"#ebcb8b".into(),
|
||||
"#bf616a".into(),
|
||||
]),
|
||||
rx_color: Some("#81a1c1".into()),
|
||||
tx_color: Some("#d08770".into()),
|
||||
rx_total_color: Some("#5e81ac".into()),
|
||||
tx_total_color: Some("#8fbcbb".into()),
|
||||
border_color: Some("#2e3440".into()),
|
||||
highlighted_border_color: Some("#5e81ac".into()),
|
||||
disabled_text_color: Some("#d8dee9".into()),
|
||||
text_color: Some("#2e3440".into()),
|
||||
selected_text_color: Some("#f5f5f5".into()),
|
||||
selected_bg_color: Some("#5e81ac".into()),
|
||||
widget_title_color: Some("#2e3440".into()),
|
||||
graph_color: Some("#2e3440".into()),
|
||||
high_battery_color: Some("#a3be8c".into()),
|
||||
medium_battery_color: Some("#ebcb8b".into()),
|
||||
low_battery_color: Some("#bf616a".into()),
|
||||
}
|
||||
}
|
||||
|
||||
// Help text
|
||||
pub const HELP_CONTENTS_TEXT: [&str; 10] = [
|
||||
"Either scroll or press the number key to go to the corresponding help menu section:",
|
||||
|
@ -573,8 +317,6 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom. Al
|
|||
#battery = false
|
||||
# Disable mouse clicks
|
||||
#disable_click = false
|
||||
# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"
|
||||
#color = "default"
|
||||
# Show memory values in the processes widget as values by default
|
||||
#process_memory_as_value = false
|
||||
# Show tree mode by default in the processes widget.
|
||||
|
@ -649,43 +391,57 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom. Al
|
|||
|
||||
# These are all the components that support custom theming. Note that colour support
|
||||
# will depend on terminal support.
|
||||
#[colors] # Uncomment if you want to use custom colors
|
||||
# Represents the colour of table headers (processes, CPU, disks, temperature).
|
||||
#table_header_color="LightBlue"
|
||||
# Represents the colour of the label each widget has.
|
||||
#widget_title_color="Gray"
|
||||
# Represents the average CPU color.
|
||||
#avg_cpu_color="Red"
|
||||
# Represents the colour the core will use in the CPU legend and graph.
|
||||
#cpu_core_colors=["LightMagenta", "LightYellow", "LightCyan", "LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"]
|
||||
# Represents the colour RAM will use in the memory legend and graph.
|
||||
#ram_color="LightMagenta"
|
||||
# Represents the colour SWAP will use in the memory legend and graph.
|
||||
#swap_color="LightYellow"
|
||||
# Represents the colour ARC will use in the memory legend and graph.
|
||||
#arc_color="LightCyan"
|
||||
# Represents the colour the GPU will use in the legend and graph.
|
||||
#gpu_core_colors=["LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"]
|
||||
# Represents the colour rx will use in the network legend and graph.
|
||||
#rx_color="LightCyan"
|
||||
# Represents the colour tx will use in the network legend and graph.
|
||||
#tx_color="LightGreen"
|
||||
# Represents the colour of the border of unselected widgets.
|
||||
#border_color="Gray"
|
||||
# Represents the colour of the border of selected widgets.
|
||||
#highlighted_border_color="LightBlue"
|
||||
# Represents the colour of most text.
|
||||
#text_color="Gray"
|
||||
# Represents the colour of text that is selected.
|
||||
#selected_text_color="Black"
|
||||
# Represents the background colour of text that is selected.
|
||||
#selected_bg_color="LightBlue"
|
||||
# Represents the colour of the lines and text of the graph.
|
||||
#graph_color="Gray"
|
||||
# Represents the colours of the battery based on charge
|
||||
#high_battery_color="green"
|
||||
#medium_battery_color="yellow"
|
||||
#low_battery_color="red"
|
||||
#[styles] # Uncomment if you want to use custom styling
|
||||
|
||||
# Built-in themes. Valid values are:
|
||||
# - "default"
|
||||
# - "default-light"
|
||||
# - "gruvbox"
|
||||
# - "gruvbox-light"
|
||||
# - "nord"
|
||||
# - "nord-light".
|
||||
#
|
||||
# This will have the lowest precedence if a custom colour palette is set,
|
||||
# or overriden if the command-line flag for a built-in theme is set.
|
||||
#theme = "default"
|
||||
|
||||
#[styles.cpu]
|
||||
#all_entry_color = "green"
|
||||
#avg_entry_color = "red"
|
||||
#cpu_core_colors = ["light magenta", "light yellow", "light cyan", "light green", "light blue", "cyan", "green", "blue"]
|
||||
|
||||
#[styles.memory]
|
||||
#ram = "light magenta"
|
||||
#cache = "light red"
|
||||
#swap = "light yellow"
|
||||
#arc = "light cyan"
|
||||
#gpus = ["light blue", "light red", "cyan", "green", "blue", "red"]
|
||||
|
||||
#[styles.network]
|
||||
#rx = "light magenta"
|
||||
#tx = "light yellow"
|
||||
#rx_total = "light cyan"
|
||||
#tx_total = "light green"
|
||||
|
||||
#[styles.battery]
|
||||
#high_battery = "green"
|
||||
#medium_battery = "yellow"
|
||||
#low_battery = "red"
|
||||
|
||||
#[styles.tables]
|
||||
#headers = {color = "light blue"}
|
||||
|
||||
#[styles.graphs]
|
||||
#graph_color = "gray"
|
||||
#legend_text = {color = "gray"}
|
||||
|
||||
#[styles.widgets]
|
||||
#border = "gray"
|
||||
#selected_border = "light blue"
|
||||
#widget_title = {color = "gray"}
|
||||
#text = {color = "gray"}
|
||||
#selected_text = {color = "black", bg_color = "light blue"}
|
||||
#disabled_text = {color = "dark gray"}
|
||||
|
||||
# Layout - layouts follow a pattern like this:
|
||||
# [[row]] represents a row in the application.
|
||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -37,7 +37,6 @@ use std::{
|
|||
|
||||
use anyhow::Context;
|
||||
use app::{layout_manager::UsedWidgets, App, AppConfigFields, DataFilters};
|
||||
use canvas::styling::CanvasStyling;
|
||||
use crossterm::{
|
||||
event::{
|
||||
poll, read, DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste,
|
||||
|
@ -49,7 +48,7 @@ use crossterm::{
|
|||
};
|
||||
use data_conversion::*;
|
||||
use event::{handle_key_event_or_break, handle_mouse_event, BottomEvent, CollectionThreadEvent};
|
||||
use options::{args, get_color_scheme, get_config_path, get_or_create_config, init_app};
|
||||
use options::{args, get_config_path, get_or_create_config, init_app};
|
||||
use tui::{backend::CrosstermBackend, Terminal};
|
||||
#[allow(unused_imports)]
|
||||
use utils::logging::*;
|
||||
|
@ -278,7 +277,7 @@ fn create_collection_thread(
|
|||
|
||||
#[cfg(feature = "generate_schema")]
|
||||
fn generate_schema() -> anyhow::Result<()> {
|
||||
let mut schema = schemars::schema_for!(crate::options::config::ConfigV1);
|
||||
let mut schema = schemars::schema_for!(crate::options::config::Config);
|
||||
{
|
||||
use itertools::Itertools;
|
||||
use strum::VariantArray;
|
||||
|
@ -336,15 +335,8 @@ fn main() -> anyhow::Result<()> {
|
|||
.context("Unable to parse or create the config file.")?
|
||||
};
|
||||
|
||||
// FIXME: Should move this into build app or config
|
||||
let styling = {
|
||||
let colour_scheme = get_color_scheme(&args, &config)?;
|
||||
CanvasStyling::new(colour_scheme, &config)?
|
||||
};
|
||||
|
||||
// Create an "app" struct, which will control most of the program and store
|
||||
// settings/state
|
||||
let (mut app, widget_layout) = init_app(args, config, &styling)?;
|
||||
// Create the "app" and initialize a bunch of stuff.
|
||||
let (mut app, widget_layout, styling) = init_app(args, config)?;
|
||||
|
||||
// Create painter and set colours.
|
||||
let mut painter = canvas::Painter::init(widget_layout, styling)?;
|
||||
|
|
109
src/options.rs
109
src/options.rs
|
@ -3,7 +3,6 @@
|
|||
// TODO: Break this apart or do something a bit smarter.
|
||||
|
||||
pub mod args;
|
||||
pub mod colours;
|
||||
pub mod config;
|
||||
mod error;
|
||||
|
||||
|
@ -17,8 +16,8 @@ use std::{
|
|||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
pub use colours::ColoursConfig;
|
||||
pub use config::ConfigV1;
|
||||
use config::style::ColourPalette;
|
||||
pub use config::Config;
|
||||
pub(crate) use error::{OptionError, OptionResult};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use indexmap::IndexSet;
|
||||
|
@ -32,7 +31,7 @@ use self::{
|
|||
};
|
||||
use crate::{
|
||||
app::{filter::Filter, layout_manager::*, *},
|
||||
canvas::{components::time_chart::LegendPosition, styling::CanvasStyling, ColourScheme},
|
||||
canvas::components::time_chart::LegendPosition,
|
||||
constants::*,
|
||||
data_collection::temperature::TemperatureType,
|
||||
utils::data_units::DataUnit,
|
||||
|
@ -97,7 +96,7 @@ pub fn get_config_path(override_config_path: Option<&Path>) -> Option<PathBuf> {
|
|||
/// path, it will try to create a new file with the default settings, and return
|
||||
/// the default config. If bottom fails to write a new config, it will silently
|
||||
/// just return the default config.
|
||||
pub fn get_or_create_config(config_path: Option<&Path>) -> OptionResult<ConfigV1> {
|
||||
pub fn get_or_create_config(config_path: Option<&Path>) -> OptionResult<Config> {
|
||||
match &config_path {
|
||||
Some(path) => {
|
||||
if let Ok(config_string) = fs::read_to_string(path) {
|
||||
|
@ -108,7 +107,7 @@ pub fn get_or_create_config(config_path: Option<&Path>) -> OptionResult<ConfigV1
|
|||
}
|
||||
|
||||
fs::File::create(path)?.write_all(CONFIG_TEXT.as_bytes())?;
|
||||
Ok(ConfigV1::default())
|
||||
Ok(Config::default())
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
@ -116,14 +115,14 @@ pub fn get_or_create_config(config_path: Option<&Path>) -> OptionResult<ConfigV1
|
|||
// but don't write to any file.
|
||||
//
|
||||
// TODO: Maybe make this "show" an error, but don't crash.
|
||||
Ok(ConfigV1::default())
|
||||
Ok(Config::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_app(
|
||||
args: BottomArgs, config: ConfigV1, styling: &CanvasStyling,
|
||||
) -> Result<(App, BottomLayout)> {
|
||||
pub(crate) fn init_app(
|
||||
args: BottomArgs, config: Config,
|
||||
) -> Result<(App, BottomLayout, ColourPalette)> {
|
||||
use BottomWidgetType::*;
|
||||
|
||||
// Since everything takes a reference, but we want to take ownership here to
|
||||
|
@ -131,6 +130,8 @@ pub fn init_app(
|
|||
let args = &args;
|
||||
let config = &config;
|
||||
|
||||
let styling = ColourPalette::new(args, config)?;
|
||||
|
||||
let (widget_layout, default_widget_id, default_widget_type_option) =
|
||||
get_widget_layout(args, config)
|
||||
.context("Found an issue while trying to build the widget layout.")?;
|
||||
|
@ -285,7 +286,7 @@ pub fn init_app(
|
|||
.unwrap_or_default(),
|
||||
default_time_value,
|
||||
autohide_timer,
|
||||
styling,
|
||||
&styling,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -318,7 +319,7 @@ pub fn init_app(
|
|||
&app_config_fields,
|
||||
mode,
|
||||
table_config,
|
||||
styling,
|
||||
&styling,
|
||||
&proc_columns,
|
||||
),
|
||||
);
|
||||
|
@ -326,13 +327,13 @@ pub fn init_app(
|
|||
Disk => {
|
||||
disk_state_map.insert(
|
||||
widget.widget_id,
|
||||
DiskTableWidget::new(&app_config_fields, styling),
|
||||
DiskTableWidget::new(&app_config_fields, &styling),
|
||||
);
|
||||
}
|
||||
Temp => {
|
||||
temp_state_map.insert(
|
||||
widget.widget_id,
|
||||
TempWidgetState::new(&app_config_fields, styling),
|
||||
TempWidgetState::new(&app_config_fields, &styling),
|
||||
);
|
||||
}
|
||||
Battery => {
|
||||
|
@ -439,11 +440,12 @@ pub fn init_app(
|
|||
is_expanded,
|
||||
),
|
||||
widget_layout,
|
||||
styling,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_widget_layout(
|
||||
args: &BottomArgs, config: &ConfigV1,
|
||||
args: &BottomArgs, config: &Config,
|
||||
) -> OptionResult<(BottomLayout, u64, Option<BottomWidgetType>)> {
|
||||
let cpu_left_legend = is_flag_enabled!(cpu_left_legend, args.cpu, config);
|
||||
|
||||
|
@ -461,7 +463,7 @@ pub fn get_widget_layout(
|
|||
Some(r) => r,
|
||||
None => {
|
||||
// This cannot (like it really shouldn't) fail!
|
||||
ref_row = toml_edit::de::from_str::<ConfigV1>(if get_use_battery(args, config) {
|
||||
ref_row = toml_edit::de::from_str::<Config>(if get_use_battery(args, config) {
|
||||
DEFAULT_BATTERY_LAYOUT
|
||||
} else {
|
||||
DEFAULT_LAYOUT
|
||||
|
@ -592,7 +594,7 @@ macro_rules! parse_ms_option {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn get_update_rate(args: &BottomArgs, config: &ConfigV1) -> OptionResult<u64> {
|
||||
fn get_update_rate(args: &BottomArgs, config: &Config) -> OptionResult<u64> {
|
||||
parse_ms_option!(
|
||||
&args.general.rate,
|
||||
config.flags.as_ref().and_then(|flags| flags.rate.as_ref()),
|
||||
|
@ -603,7 +605,7 @@ fn get_update_rate(args: &BottomArgs, config: &ConfigV1) -> OptionResult<u64> {
|
|||
)
|
||||
}
|
||||
|
||||
fn get_temperature(args: &BottomArgs, config: &ConfigV1) -> OptionResult<TemperatureType> {
|
||||
fn get_temperature(args: &BottomArgs, config: &Config) -> OptionResult<TemperatureType> {
|
||||
if args.temperature.fahrenheit {
|
||||
return Ok(TemperatureType::Fahrenheit);
|
||||
} else if args.temperature.kelvin {
|
||||
|
@ -619,7 +621,7 @@ fn get_temperature(args: &BottomArgs, config: &ConfigV1) -> OptionResult<Tempera
|
|||
}
|
||||
|
||||
/// Yes, this function gets whether to show average CPU (true) or not (false).
|
||||
fn get_show_average_cpu(args: &BottomArgs, config: &ConfigV1) -> bool {
|
||||
fn get_show_average_cpu(args: &BottomArgs, config: &Config) -> bool {
|
||||
if args.cpu.hide_avg_cpu {
|
||||
return false;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
|
@ -633,7 +635,7 @@ fn get_show_average_cpu(args: &BottomArgs, config: &ConfigV1) -> bool {
|
|||
|
||||
#[inline]
|
||||
fn get_default_time_value(
|
||||
args: &BottomArgs, config: &ConfigV1, retention_ms: u64,
|
||||
args: &BottomArgs, config: &Config, retention_ms: u64,
|
||||
) -> OptionResult<u64> {
|
||||
parse_ms_option!(
|
||||
&args.general.default_time_value,
|
||||
|
@ -649,7 +651,7 @@ fn get_default_time_value(
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn get_time_interval(args: &BottomArgs, config: &ConfigV1, retention_ms: u64) -> OptionResult<u64> {
|
||||
fn get_time_interval(args: &BottomArgs, config: &Config, retention_ms: u64) -> OptionResult<u64> {
|
||||
parse_ms_option!(
|
||||
&args.general.time_delta,
|
||||
config
|
||||
|
@ -664,7 +666,7 @@ fn get_time_interval(args: &BottomArgs, config: &ConfigV1, retention_ms: u64) ->
|
|||
}
|
||||
|
||||
fn get_default_widget_and_count(
|
||||
args: &BottomArgs, config: &ConfigV1,
|
||||
args: &BottomArgs, config: &Config,
|
||||
) -> OptionResult<(Option<BottomWidgetType>, u64)> {
|
||||
let widget_type = if let Some(widget_type) = &args.general.default_widget_type {
|
||||
let parsed_widget = parse_arg_value!(widget_type.parse(), "default_widget_type")?;
|
||||
|
@ -714,7 +716,7 @@ fn get_default_widget_and_count(
|
|||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn get_use_battery(args: &BottomArgs, config: &ConfigV1) -> bool {
|
||||
fn get_use_battery(args: &BottomArgs, config: &Config) -> bool {
|
||||
#[cfg(feature = "battery")]
|
||||
{
|
||||
// TODO: Move this so it's dynamic in the app itself and automatically hide if
|
||||
|
@ -740,7 +742,7 @@ fn get_use_battery(args: &BottomArgs, config: &ConfigV1) -> bool {
|
|||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn get_enable_gpu(args: &BottomArgs, config: &ConfigV1) -> bool {
|
||||
fn get_enable_gpu(args: &BottomArgs, config: &Config) -> bool {
|
||||
#[cfg(feature = "gpu")]
|
||||
{
|
||||
if args.gpu.enable_gpu {
|
||||
|
@ -756,7 +758,7 @@ fn get_enable_gpu(args: &BottomArgs, config: &ConfigV1) -> bool {
|
|||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn get_enable_cache_memory(args: &BottomArgs, config: &ConfigV1) -> bool {
|
||||
fn get_enable_cache_memory(args: &BottomArgs, config: &Config) -> bool {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
if args.memory.enable_cache_memory {
|
||||
|
@ -810,32 +812,7 @@ fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> OptionResult<Option<Filt
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_color_scheme(args: &BottomArgs, config: &ConfigV1) -> OptionResult<ColourScheme> {
|
||||
if let Some(color) = &args.style.color {
|
||||
// Highest priority is always command line flags...
|
||||
return ColourScheme::from_str(color);
|
||||
} else if let Some(colors) = &config.colors {
|
||||
if !colors.is_empty() {
|
||||
// Then, give priority to custom colours...
|
||||
return Ok(ColourScheme::Custom);
|
||||
} else if let Some(flags) = &config.flags {
|
||||
// Last priority is config file flags...
|
||||
if let Some(color) = &flags.color {
|
||||
return ColourScheme::from_str(color);
|
||||
}
|
||||
}
|
||||
} else if let Some(flags) = &config.flags {
|
||||
// Last priority is config file flags...
|
||||
if let Some(color) = &flags.color {
|
||||
return ColourScheme::from_str(color);
|
||||
}
|
||||
}
|
||||
|
||||
// And lastly, the final case is just "default".
|
||||
Ok(ColourScheme::Default)
|
||||
}
|
||||
|
||||
fn get_network_unit_type(args: &BottomArgs, config: &ConfigV1) -> DataUnit {
|
||||
fn get_network_unit_type(args: &BottomArgs, config: &Config) -> DataUnit {
|
||||
if args.network.network_use_bytes {
|
||||
return DataUnit::Byte;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
|
@ -849,7 +826,7 @@ fn get_network_unit_type(args: &BottomArgs, config: &ConfigV1) -> DataUnit {
|
|||
DataUnit::Bit
|
||||
}
|
||||
|
||||
fn get_network_scale_type(args: &BottomArgs, config: &ConfigV1) -> AxisScaling {
|
||||
fn get_network_scale_type(args: &BottomArgs, config: &Config) -> AxisScaling {
|
||||
if args.network.network_use_log {
|
||||
return AxisScaling::Log;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
|
@ -863,7 +840,7 @@ fn get_network_scale_type(args: &BottomArgs, config: &ConfigV1) -> AxisScaling {
|
|||
AxisScaling::Linear
|
||||
}
|
||||
|
||||
fn get_retention(args: &BottomArgs, config: &ConfigV1) -> OptionResult<u64> {
|
||||
fn get_retention(args: &BottomArgs, config: &Config) -> OptionResult<u64> {
|
||||
const DEFAULT_RETENTION_MS: u64 = 600 * 1000; // Keep 10 minutes of data.
|
||||
|
||||
parse_ms_option!(
|
||||
|
@ -880,7 +857,7 @@ fn get_retention(args: &BottomArgs, config: &ConfigV1) -> OptionResult<u64> {
|
|||
}
|
||||
|
||||
fn get_network_legend_position(
|
||||
args: &BottomArgs, config: &ConfigV1,
|
||||
args: &BottomArgs, config: &Config,
|
||||
) -> OptionResult<Option<LegendPosition>> {
|
||||
let result = if let Some(s) = &args.network.network_legend {
|
||||
match s.to_ascii_lowercase().trim() {
|
||||
|
@ -901,7 +878,7 @@ fn get_network_legend_position(
|
|||
}
|
||||
|
||||
fn get_memory_legend_position(
|
||||
args: &BottomArgs, config: &ConfigV1,
|
||||
args: &BottomArgs, config: &Config,
|
||||
) -> OptionResult<Option<LegendPosition>> {
|
||||
let result = if let Some(s) = &args.memory.memory_legend {
|
||||
match s.to_ascii_lowercase().trim() {
|
||||
|
@ -925,13 +902,12 @@ fn get_memory_legend_position(
|
|||
mod test {
|
||||
use clap::Parser;
|
||||
|
||||
use super::{get_color_scheme, get_time_interval, ConfigV1};
|
||||
use super::{get_time_interval, Config};
|
||||
use crate::{
|
||||
app::App,
|
||||
args::BottomArgs,
|
||||
canvas::styling::CanvasStyling,
|
||||
options::{
|
||||
config::FlagConfig, get_default_time_value, get_retention, get_update_rate,
|
||||
config::flags::FlagConfig, get_default_time_value, get_retention, get_update_rate,
|
||||
try_parse_ms,
|
||||
},
|
||||
};
|
||||
|
@ -957,7 +933,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn matches_human_times() {
|
||||
let config = ConfigV1::default();
|
||||
let config = Config::default();
|
||||
|
||||
{
|
||||
let delta_args = vec!["btm", "--time_delta", "2 min"];
|
||||
|
@ -982,7 +958,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn matches_number_times() {
|
||||
let config = ConfigV1::default();
|
||||
let config = Config::default();
|
||||
|
||||
{
|
||||
let delta_args = vec!["btm", "--time_delta", "120000"];
|
||||
|
@ -1009,7 +985,7 @@ mod test {
|
|||
fn config_human_times() {
|
||||
let args = BottomArgs::parse_from(["btm"]);
|
||||
|
||||
let mut config = ConfigV1::default();
|
||||
let mut config = Config::default();
|
||||
let flags = FlagConfig {
|
||||
time_delta: Some("2 min".to_string().into()),
|
||||
default_time_value: Some("300s".to_string().into()),
|
||||
|
@ -1039,7 +1015,7 @@ mod test {
|
|||
fn config_number_times_as_string() {
|
||||
let args = BottomArgs::parse_from(["btm"]);
|
||||
|
||||
let mut config = ConfigV1::default();
|
||||
let mut config = Config::default();
|
||||
let flags = FlagConfig {
|
||||
time_delta: Some("120000".to_string().into()),
|
||||
default_time_value: Some("300000".to_string().into()),
|
||||
|
@ -1069,7 +1045,7 @@ mod test {
|
|||
fn config_number_times_as_num() {
|
||||
let args = BottomArgs::parse_from(["btm"]);
|
||||
|
||||
let mut config = ConfigV1::default();
|
||||
let mut config = Config::default();
|
||||
let flags = FlagConfig {
|
||||
time_delta: Some(120000.into()),
|
||||
default_time_value: Some(300000.into()),
|
||||
|
@ -1096,11 +1072,8 @@ mod test {
|
|||
}
|
||||
|
||||
fn create_app(args: BottomArgs) -> App {
|
||||
let config = ConfigV1::default();
|
||||
let styling =
|
||||
CanvasStyling::new(get_color_scheme(&args, &config).unwrap(), &config).unwrap();
|
||||
|
||||
super::init_app(args, config, &styling).unwrap().0
|
||||
let config = Config::default();
|
||||
super::init_app(args, config).unwrap().0
|
||||
}
|
||||
|
||||
// TODO: There's probably a better way to create clap options AND unify together
|
||||
|
|
|
@ -542,10 +542,10 @@ pub struct StyleArgs {
|
|||
],
|
||||
hide_possible_values = true,
|
||||
help = indoc! {
|
||||
"Use a color scheme, use '--help' for info on the colors. [possible values: default, default-light, gruvbox, gruvbox-light, nord, nord-light]",
|
||||
"Use a built-in color theme, use '--help' for info on the colors. [possible values: default, default-light, gruvbox, gruvbox-light, nord, nord-light]",
|
||||
},
|
||||
long_help = indoc! {
|
||||
"Use a pre-defined color scheme. Currently supported values are:
|
||||
"Use a pre-defined color theme. Currently supported themes are:
|
||||
- default
|
||||
- default-light (default but adjusted for lighter backgrounds)
|
||||
- gruvbox (a bright theme with 'retro groove' colors)
|
||||
|
@ -554,7 +554,7 @@ pub struct StyleArgs {
|
|||
- nord-light (nord but adjusted for lighter backgrounds)"
|
||||
}
|
||||
)]
|
||||
pub color: Option<String>,
|
||||
pub theme: Option<String>,
|
||||
}
|
||||
|
||||
/// Other arguments. This just handle options that are for help/version
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Colour configuration.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub struct ColoursConfig {
|
||||
// TODO: Make these an enum instead.
|
||||
pub table_header_color: Option<Cow<'static, str>>,
|
||||
pub all_cpu_color: Option<Cow<'static, str>>,
|
||||
pub avg_cpu_color: Option<Cow<'static, str>>,
|
||||
pub cpu_core_colors: Option<Vec<Cow<'static, str>>>,
|
||||
pub ram_color: Option<Cow<'static, str>>,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub cache_color: Option<Cow<'static, str>>,
|
||||
pub swap_color: Option<Cow<'static, str>>,
|
||||
pub arc_color: Option<Cow<'static, str>>,
|
||||
pub gpu_core_colors: Option<Vec<Cow<'static, str>>>,
|
||||
pub rx_color: Option<Cow<'static, str>>,
|
||||
pub tx_color: Option<Cow<'static, str>>,
|
||||
pub rx_total_color: Option<Cow<'static, str>>, // These only affect basic mode.
|
||||
pub tx_total_color: Option<Cow<'static, str>>, // These only affect basic mode.
|
||||
pub border_color: Option<Cow<'static, str>>,
|
||||
pub highlighted_border_color: Option<Cow<'static, str>>,
|
||||
pub disabled_text_color: Option<Cow<'static, str>>,
|
||||
pub text_color: Option<Cow<'static, str>>,
|
||||
pub selected_text_color: Option<Cow<'static, str>>,
|
||||
pub selected_bg_color: Option<Cow<'static, str>>,
|
||||
pub widget_title_color: Option<Cow<'static, str>>,
|
||||
pub graph_color: Option<Cow<'static, str>>,
|
||||
pub high_battery_color: Option<Cow<'static, str>>,
|
||||
pub medium_battery_color: Option<Cow<'static, str>>,
|
||||
pub low_battery_color: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
impl ColoursConfig {
|
||||
/// Returns `true` if there is a [`ConfigColours`] that is empty or there
|
||||
/// isn't one at all.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
if let Ok(serialized_string) = toml_edit::ser::to_string(self) {
|
||||
return serialized_string.is_empty();
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
|
@ -1,19 +1,22 @@
|
|||
pub mod cpu;
|
||||
pub mod disk;
|
||||
pub mod flags;
|
||||
mod ignore_list;
|
||||
pub mod layout;
|
||||
pub mod network;
|
||||
pub mod process;
|
||||
pub mod style;
|
||||
pub mod temperature;
|
||||
|
||||
use disk::DiskConfig;
|
||||
use flags::FlagConfig;
|
||||
use network::NetworkConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use style::StyleConfig;
|
||||
use temperature::TempConfig;
|
||||
|
||||
pub use self::ignore_list::IgnoreList;
|
||||
use self::{cpu::CpuConfig, layout::Row, process::ProcessesConfig};
|
||||
use super::ColoursConfig;
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[cfg_attr(
|
||||
|
@ -21,9 +24,9 @@ use super::ColoursConfig;
|
|||
derive(schemars::JsonSchema),
|
||||
schemars(title = "Schema for bottom's configs (nightly)")
|
||||
)]
|
||||
pub struct ConfigV1 {
|
||||
pub struct Config {
|
||||
pub(crate) flags: Option<FlagConfig>,
|
||||
pub(crate) colors: Option<ColoursConfig>,
|
||||
pub(crate) styles: Option<StyleConfig>,
|
||||
pub(crate) row: Option<Vec<Row>>,
|
||||
pub(crate) processes: Option<ProcessesConfig>,
|
||||
pub(crate) disk: Option<DiskConfig>,
|
||||
|
@ -51,47 +54,3 @@ impl From<u64> for StringOrNum {
|
|||
StringOrNum::Num(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct FlagConfig {
|
||||
pub(crate) hide_avg_cpu: Option<bool>,
|
||||
pub(crate) dot_marker: Option<bool>,
|
||||
pub(crate) temperature_type: Option<String>,
|
||||
pub(crate) rate: Option<StringOrNum>,
|
||||
pub(crate) cpu_left_legend: Option<bool>,
|
||||
pub(crate) current_usage: Option<bool>,
|
||||
pub(crate) unnormalized_cpu: Option<bool>,
|
||||
pub(crate) group_processes: Option<bool>,
|
||||
pub(crate) case_sensitive: Option<bool>,
|
||||
pub(crate) whole_word: Option<bool>,
|
||||
pub(crate) regex: Option<bool>,
|
||||
pub(crate) basic: Option<bool>,
|
||||
pub(crate) default_time_value: Option<StringOrNum>,
|
||||
pub(crate) time_delta: Option<StringOrNum>,
|
||||
pub(crate) autohide_time: Option<bool>,
|
||||
pub(crate) hide_time: Option<bool>,
|
||||
pub(crate) default_widget_type: Option<String>,
|
||||
pub(crate) default_widget_count: Option<u64>,
|
||||
pub(crate) expanded: Option<bool>,
|
||||
pub(crate) use_old_network_legend: Option<bool>,
|
||||
pub(crate) hide_table_gap: Option<bool>,
|
||||
pub(crate) battery: Option<bool>,
|
||||
pub(crate) disable_click: Option<bool>,
|
||||
pub(crate) no_write: Option<bool>,
|
||||
pub(crate) network_legend: Option<String>,
|
||||
pub(crate) memory_legend: Option<String>,
|
||||
/// For built-in colour palettes.
|
||||
pub(crate) color: Option<String>,
|
||||
pub(crate) process_memory_as_value: Option<bool>,
|
||||
pub(crate) tree: Option<bool>,
|
||||
pub(crate) show_table_scroll_position: Option<bool>,
|
||||
pub(crate) process_command: Option<bool>,
|
||||
pub(crate) disable_advanced_kill: Option<bool>,
|
||||
pub(crate) network_use_bytes: Option<bool>,
|
||||
pub(crate) network_use_log: Option<bool>,
|
||||
pub(crate) network_use_binary_prefix: Option<bool>,
|
||||
pub(crate) enable_gpu: Option<bool>,
|
||||
pub(crate) enable_cache_memory: Option<bool>,
|
||||
pub(crate) retention: Option<StringOrNum>,
|
||||
}
|
||||
|
|
45
src/options/config/flags.rs
Normal file
45
src/options/config/flags.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::StringOrNum;
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct FlagConfig {
|
||||
pub(crate) hide_avg_cpu: Option<bool>,
|
||||
pub(crate) dot_marker: Option<bool>,
|
||||
pub(crate) temperature_type: Option<String>,
|
||||
pub(crate) rate: Option<StringOrNum>,
|
||||
pub(crate) cpu_left_legend: Option<bool>,
|
||||
pub(crate) current_usage: Option<bool>,
|
||||
pub(crate) unnormalized_cpu: Option<bool>,
|
||||
pub(crate) group_processes: Option<bool>,
|
||||
pub(crate) case_sensitive: Option<bool>,
|
||||
pub(crate) whole_word: Option<bool>,
|
||||
pub(crate) regex: Option<bool>,
|
||||
pub(crate) basic: Option<bool>,
|
||||
pub(crate) default_time_value: Option<StringOrNum>,
|
||||
pub(crate) time_delta: Option<StringOrNum>,
|
||||
pub(crate) autohide_time: Option<bool>,
|
||||
pub(crate) hide_time: Option<bool>,
|
||||
pub(crate) default_widget_type: Option<String>,
|
||||
pub(crate) default_widget_count: Option<u64>,
|
||||
pub(crate) expanded: Option<bool>,
|
||||
pub(crate) use_old_network_legend: Option<bool>,
|
||||
pub(crate) hide_table_gap: Option<bool>,
|
||||
pub(crate) battery: Option<bool>,
|
||||
pub(crate) disable_click: Option<bool>,
|
||||
pub(crate) no_write: Option<bool>,
|
||||
pub(crate) network_legend: Option<String>,
|
||||
pub(crate) memory_legend: Option<String>,
|
||||
pub(crate) process_memory_as_value: Option<bool>,
|
||||
pub(crate) tree: Option<bool>,
|
||||
pub(crate) show_table_scroll_position: Option<bool>,
|
||||
pub(crate) process_command: Option<bool>,
|
||||
pub(crate) disable_advanced_kill: Option<bool>,
|
||||
pub(crate) network_use_bytes: Option<bool>,
|
||||
pub(crate) network_use_log: Option<bool>,
|
||||
pub(crate) network_use_binary_prefix: Option<bool>,
|
||||
pub(crate) enable_gpu: Option<bool>,
|
||||
pub(crate) enable_cache_memory: Option<bool>,
|
||||
pub(crate) retention: Option<StringOrNum>,
|
||||
}
|
|
@ -242,7 +242,7 @@ mod test {
|
|||
use super::*;
|
||||
use crate::{
|
||||
constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID},
|
||||
options::ConfigV1,
|
||||
options::Config,
|
||||
};
|
||||
|
||||
const PROC_LAYOUT: &str = r#"
|
||||
|
@ -295,7 +295,7 @@ mod test {
|
|||
#[test]
|
||||
/// Tests the default setup.
|
||||
fn test_default_movement() {
|
||||
let rows = from_str::<ConfigV1>(DEFAULT_LAYOUT).unwrap().row.unwrap();
|
||||
let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap();
|
||||
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
|
||||
|
||||
// Simple tests for the top CPU widget
|
||||
|
@ -369,7 +369,7 @@ mod test {
|
|||
fn test_default_battery_movement() {
|
||||
use crate::constants::DEFAULT_BATTERY_LAYOUT;
|
||||
|
||||
let rows = from_str::<ConfigV1>(DEFAULT_BATTERY_LAYOUT)
|
||||
let rows = from_str::<Config>(DEFAULT_BATTERY_LAYOUT)
|
||||
.unwrap()
|
||||
.row
|
||||
.unwrap();
|
||||
|
@ -415,7 +415,7 @@ mod test {
|
|||
#[test]
|
||||
/// Tests using cpu_left_legend.
|
||||
fn test_cpu_left_legend() {
|
||||
let rows = from_str::<ConfigV1>(DEFAULT_LAYOUT).unwrap().row.unwrap();
|
||||
let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap();
|
||||
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, true);
|
||||
|
||||
// Legend
|
||||
|
@ -475,7 +475,7 @@ mod test {
|
|||
type="proc"
|
||||
"#;
|
||||
|
||||
let rows = from_str::<ConfigV1>(proc_layout).unwrap().row.unwrap();
|
||||
let rows = from_str::<Config>(proc_layout).unwrap().row.unwrap();
|
||||
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||
let mut total_height_ratio = 0;
|
||||
let mut default_widget_count = 1;
|
||||
|
@ -508,7 +508,7 @@ mod test {
|
|||
#[test]
|
||||
/// Tests default widget by setting type and count.
|
||||
fn test_default_widget_by_option() {
|
||||
let rows = from_str::<ConfigV1>(PROC_LAYOUT).unwrap().row.unwrap();
|
||||
let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap();
|
||||
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||
let mut total_height_ratio = 0;
|
||||
let mut default_widget_count = 3;
|
||||
|
@ -540,7 +540,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_proc_custom_layout() {
|
||||
let rows = from_str::<ConfigV1>(PROC_LAYOUT).unwrap().row.unwrap();
|
||||
let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap();
|
||||
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
|
||||
|
||||
// First proc widget
|
||||
|
|
260
src/options/config/style.rs
Normal file
260
src/options/config/style.rs
Normal file
|
@ -0,0 +1,260 @@
|
|||
//! Config options around styling.
|
||||
|
||||
mod battery;
|
||||
mod cpu;
|
||||
mod graphs;
|
||||
mod memory;
|
||||
mod network;
|
||||
mod tables;
|
||||
mod themes;
|
||||
mod utils;
|
||||
mod widgets;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use battery::BatteryStyle;
|
||||
use cpu::CpuStyle;
|
||||
use graphs::GraphStyle;
|
||||
use memory::MemoryStyle;
|
||||
use network::NetworkStyle;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tables::TableStyle;
|
||||
use tui::style::Style;
|
||||
use utils::{opt, set_colour, set_colour_list, set_style};
|
||||
use widgets::WidgetStyle;
|
||||
|
||||
use crate::options::{args::BottomArgs, OptionError, OptionResult};
|
||||
|
||||
use super::Config;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct ColorStr(Cow<'static, str>);
|
||||
|
||||
/// A style for text.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct TextStyleConfig {
|
||||
/// A built-in ANSI colour, RGB hex, or RGB colour code.
|
||||
#[serde(alias = "colour")]
|
||||
pub(crate) color: Option<ColorStr>,
|
||||
|
||||
/// A built-in ANSI colour, RGB hex, or RGB colour code.
|
||||
#[serde(alias = "bg_colour")]
|
||||
pub(crate) bg_color: Option<ColorStr>,
|
||||
|
||||
/// Whether to make this text bolded or not. If not set,
|
||||
/// will default to built-in defaults.
|
||||
pub(crate) bold: Option<bool>,
|
||||
}
|
||||
|
||||
/// Style-related configs.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct StyleConfig {
|
||||
/// A built-in theme.
|
||||
///
|
||||
/// If this is and a custom colour are both set, in the config file,
|
||||
/// the custom colour scheme will be prioritized first. If a theme
|
||||
/// is set in the command-line args, however, it will always be
|
||||
/// prioritized first.
|
||||
pub(crate) theme: Option<Cow<'static, str>>,
|
||||
|
||||
/// Styling for the CPU widget.
|
||||
pub(crate) cpu: Option<CpuStyle>,
|
||||
|
||||
/// Styling for the memory widget.
|
||||
pub(crate) memory: Option<MemoryStyle>,
|
||||
|
||||
/// Styling for the network widget.
|
||||
pub(crate) network: Option<NetworkStyle>,
|
||||
|
||||
/// Styling for the battery widget.
|
||||
pub(crate) battery: Option<BatteryStyle>,
|
||||
|
||||
/// Styling for table widgets.
|
||||
pub(crate) tables: Option<TableStyle>,
|
||||
|
||||
/// Styling for graph widgets.
|
||||
pub(crate) graphs: Option<GraphStyle>,
|
||||
|
||||
/// Styling for general widgets.
|
||||
pub(crate) widgets: Option<WidgetStyle>,
|
||||
}
|
||||
|
||||
/// The actual internal representation of the configured colours,
|
||||
/// as a "palette".
|
||||
#[derive(Debug)]
|
||||
pub struct ColourPalette {
|
||||
pub selected_text_style: Style,
|
||||
pub table_header_style: Style,
|
||||
pub ram_style: Style,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub cache_style: Style,
|
||||
pub swap_style: Style,
|
||||
pub arc_style: Style,
|
||||
pub gpu_colours: Vec<Style>,
|
||||
pub rx_style: Style,
|
||||
pub tx_style: Style,
|
||||
pub total_rx_style: Style,
|
||||
pub total_tx_style: Style,
|
||||
pub all_cpu_colour: Style,
|
||||
pub avg_cpu_colour: Style,
|
||||
pub cpu_colour_styles: Vec<Style>,
|
||||
pub border_style: Style,
|
||||
pub highlighted_border_style: Style,
|
||||
pub text_style: Style,
|
||||
pub widget_title_style: Style,
|
||||
pub graph_style: Style,
|
||||
pub graph_legend_style: Style,
|
||||
pub high_battery: Style,
|
||||
pub medium_battery: Style,
|
||||
pub low_battery: Style,
|
||||
pub invalid_query_style: Style,
|
||||
pub disabled_text_style: Style,
|
||||
}
|
||||
|
||||
impl Default for ColourPalette {
|
||||
fn default() -> Self {
|
||||
Self::default_palette()
|
||||
}
|
||||
}
|
||||
|
||||
impl ColourPalette {
|
||||
pub fn new(args: &BottomArgs, config: &Config) -> anyhow::Result<Self> {
|
||||
let mut palette = match &args.style.theme {
|
||||
Some(theme) => Self::from_theme(theme)?,
|
||||
None => match config.styles.as_ref().and_then(|s| s.theme.as_ref()) {
|
||||
Some(theme) => Self::from_theme(theme)?,
|
||||
None => Self::default(),
|
||||
},
|
||||
};
|
||||
|
||||
// Apply theme from config on top.
|
||||
if let Some(style) = &config.styles {
|
||||
palette.set_colours_from_palette(style)?;
|
||||
}
|
||||
|
||||
Ok(palette)
|
||||
}
|
||||
|
||||
fn from_theme(theme: &str) -> anyhow::Result<Self> {
|
||||
let lower_case = theme.to_lowercase();
|
||||
match lower_case.as_str() {
|
||||
"default" => Ok(Self::default_palette()),
|
||||
"default-light" => Ok(Self::default_light_mode()),
|
||||
"gruvbox" => Ok(Self::gruvbox_palette()),
|
||||
"gruvbox-light" => Ok(Self::gruvbox_light_palette()),
|
||||
"nord" => Ok(Self::nord_palette()),
|
||||
"nord-light" => Ok(Self::nord_light_palette()),
|
||||
_ => Err(
|
||||
OptionError::other(format!("'{theme}' is an invalid built-in color scheme."))
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_colours_from_palette(&mut self, config: &StyleConfig) -> OptionResult<()> {
|
||||
// CPU
|
||||
set_colour!(self.avg_cpu_colour, config.cpu, avg_entry_color);
|
||||
set_colour!(self.all_cpu_colour, config.cpu, all_entry_color);
|
||||
set_colour_list!(self.cpu_colour_styles, config.cpu, cpu_core_colors);
|
||||
|
||||
// Memory
|
||||
set_colour!(self.ram_style, config.memory, ram);
|
||||
set_colour!(self.swap_style, config.memory, swap);
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
set_colour!(self.cache_style, config.memory, cache);
|
||||
|
||||
#[cfg(feature = "zfs")]
|
||||
set_colour!(self.arc_style, config.memory, arc);
|
||||
|
||||
#[cfg(feature = "gpu")]
|
||||
set_colour_list!(self.gpu_colours, config.memory, gpus);
|
||||
|
||||
// Network
|
||||
set_colour!(self.rx_style, config.network, rx);
|
||||
set_colour!(self.tx_style, config.network, tx);
|
||||
set_colour!(self.total_rx_style, config.network, rx_total);
|
||||
set_colour!(self.total_tx_style, config.network, tx_total);
|
||||
|
||||
// Battery
|
||||
set_colour!(self.high_battery, config.battery, high_battery);
|
||||
set_colour!(self.medium_battery, config.battery, medium_battery);
|
||||
set_colour!(self.low_battery, config.battery, low_battery);
|
||||
|
||||
// Tables
|
||||
set_style!(self.table_header_style, config.tables, headers);
|
||||
|
||||
// Widget graphs
|
||||
set_colour!(self.graph_style, config.graphs, graph_color);
|
||||
set_style!(self.graph_legend_style, config.graphs, legend_text);
|
||||
|
||||
// General widget text.
|
||||
set_style!(self.widget_title_style, config.widgets, widget_title);
|
||||
set_style!(self.text_style, config.widgets, text);
|
||||
set_style!(self.selected_text_style, config.widgets, selected_text);
|
||||
set_style!(self.disabled_text_style, config.widgets, disabled_text);
|
||||
|
||||
// Widget borders
|
||||
set_colour!(self.border_style, config.widgets, border);
|
||||
set_colour!(
|
||||
self.highlighted_border_style,
|
||||
config.widgets,
|
||||
selected_border
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use tui::style::{Color, Style};
|
||||
|
||||
use super::ColourPalette;
|
||||
use crate::options::config::style::utils::str_to_colour;
|
||||
|
||||
#[test]
|
||||
fn default_selected_colour_works() {
|
||||
let mut colours = ColourPalette::default();
|
||||
println!("colours: {colours:?}");
|
||||
let original_selected_text_colour = ColourPalette::default_palette()
|
||||
.selected_text_style
|
||||
.fg
|
||||
.unwrap();
|
||||
let original_selected_bg_colour = ColourPalette::default_palette()
|
||||
.selected_text_style
|
||||
.bg
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
colours.selected_text_style,
|
||||
Style::default()
|
||||
.fg(original_selected_text_colour)
|
||||
.bg(original_selected_bg_colour),
|
||||
);
|
||||
|
||||
colours.selected_text_style = colours
|
||||
.selected_text_style
|
||||
.fg(str_to_colour("magenta").unwrap())
|
||||
.bg(str_to_colour("red").unwrap());
|
||||
|
||||
assert_eq!(
|
||||
colours.selected_text_style,
|
||||
Style::default().fg(Color::Magenta).bg(Color::Red),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn built_in_colour_schemes_work() {
|
||||
ColourPalette::from_theme("default").unwrap();
|
||||
ColourPalette::from_theme("default-light").unwrap();
|
||||
ColourPalette::from_theme("gruvbox").unwrap();
|
||||
ColourPalette::from_theme("gruvbox-light").unwrap();
|
||||
ColourPalette::from_theme("nord").unwrap();
|
||||
ColourPalette::from_theme("nord-light").unwrap();
|
||||
}
|
||||
}
|
12
src/options/config/style/battery.rs
Normal file
12
src/options/config/style/battery.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ColorStr;
|
||||
|
||||
/// Styling specific to the battery widget.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct BatteryStyle {
|
||||
pub(crate) high_battery: Option<ColorStr>,
|
||||
pub(crate) medium_battery: Option<ColorStr>,
|
||||
pub(crate) low_battery: Option<ColorStr>,
|
||||
}
|
18
src/options/config/style/cpu.rs
Normal file
18
src/options/config/style/cpu.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ColorStr;
|
||||
|
||||
/// Styling specific to the CPU widget.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct CpuStyle {
|
||||
// TODO: Should I change the name of these?
|
||||
#[serde(alias = "all_entry_colour")]
|
||||
pub(crate) all_entry_color: Option<ColorStr>,
|
||||
|
||||
#[serde(alias = "avg_entry_colour")]
|
||||
pub(crate) avg_entry_color: Option<ColorStr>,
|
||||
|
||||
#[serde(alias = "cpu_core_colours")]
|
||||
pub(crate) cpu_core_colors: Option<Vec<ColorStr>>,
|
||||
}
|
13
src/options/config/style/graphs.rs
Normal file
13
src/options/config/style/graphs.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ColorStr, TextStyleConfig};
|
||||
|
||||
/// General styling for graph widgets.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct GraphStyle {
|
||||
#[serde(alias = "graph_colour")]
|
||||
pub(crate) graph_color: Option<ColorStr>,
|
||||
|
||||
pub(crate) legend_text: Option<TextStyleConfig>,
|
||||
}
|
15
src/options/config/style/memory.rs
Normal file
15
src/options/config/style/memory.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ColorStr;
|
||||
|
||||
/// Styling specific to the memory widget.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct MemoryStyle {
|
||||
pub(crate) ram: Option<ColorStr>,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(crate) cache: Option<ColorStr>,
|
||||
pub(crate) swap: Option<ColorStr>,
|
||||
pub(crate) arc: Option<ColorStr>,
|
||||
pub(crate) gpus: Option<Vec<ColorStr>>,
|
||||
}
|
19
src/options/config/style/network.rs
Normal file
19
src/options/config/style/network.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ColorStr;
|
||||
|
||||
/// Styling specific to the network widget.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct NetworkStyle {
|
||||
pub(crate) rx: Option<ColorStr>,
|
||||
pub(crate) tx: Option<ColorStr>,
|
||||
|
||||
/// Set the colour of the "rx total" text. This only affects
|
||||
/// basic mode.
|
||||
pub(crate) rx_total: Option<ColorStr>,
|
||||
|
||||
/// Set the colour of the "tx total" text. This only affects
|
||||
/// basic mode.
|
||||
pub(crate) tx_total: Option<ColorStr>,
|
||||
}
|
10
src/options/config/style/tables.rs
Normal file
10
src/options/config/style/tables.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::TextStyleConfig;
|
||||
|
||||
/// General styling for table widgets.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct TableStyle {
|
||||
pub(crate) headers: Option<TextStyleConfig>,
|
||||
}
|
20
src/options/config/style/themes.rs
Normal file
20
src/options/config/style/themes.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
//! A set of pre-defined themes.
|
||||
|
||||
pub(super) mod default;
|
||||
pub(super) mod gruvbox;
|
||||
pub(super) mod nord;
|
||||
|
||||
macro_rules! color {
|
||||
($value:expr) => {
|
||||
tui::style::Style::new().fg($value)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! hex {
|
||||
($value:literal) => {
|
||||
tui::style::Style::new()
|
||||
.fg(crate::options::config::style::utils::convert_hex_to_color($value.into()).unwrap())
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) use {color, hex};
|
110
src/options/config/style/themes/default.rs
Normal file
110
src/options/config/style/themes/default.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
use tui::style::{Color, Style};
|
||||
|
||||
use crate::options::config::style::ColourPalette;
|
||||
|
||||
use super::color;
|
||||
|
||||
impl ColourPalette {
|
||||
pub(crate) fn default_palette() -> Self {
|
||||
const FIRST_COLOUR: Color = Color::LightMagenta;
|
||||
const SECOND_COLOUR: Color = Color::LightYellow;
|
||||
const THIRD_COLOUR: Color = Color::LightCyan;
|
||||
const FOURTH_COLOUR: Color = Color::LightGreen;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const FIFTH_COLOUR: Color = Color::LightRed;
|
||||
const HIGHLIGHT_COLOUR: Color = Color::LightBlue;
|
||||
const AVG_COLOUR: Color = Color::Red;
|
||||
const ALL_COLOUR: Color = Color::Green;
|
||||
|
||||
const DEFAULT_SELECTED_TEXT_STYLE: Style = color!(Color::Black).bg(HIGHLIGHT_COLOUR);
|
||||
|
||||
const TEXT_COLOUR: Color = Color::Gray;
|
||||
|
||||
Self {
|
||||
selected_text_style: DEFAULT_SELECTED_TEXT_STYLE,
|
||||
table_header_style: color!(HIGHLIGHT_COLOUR),
|
||||
ram_style: color!(FIRST_COLOUR),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: color!(FIFTH_COLOUR),
|
||||
swap_style: color!(SECOND_COLOUR),
|
||||
arc_style: color!(THIRD_COLOUR),
|
||||
gpu_colours: vec![
|
||||
color!(FOURTH_COLOUR),
|
||||
color!(Color::LightBlue),
|
||||
color!(Color::LightRed),
|
||||
color!(Color::Cyan),
|
||||
color!(Color::Green),
|
||||
color!(Color::Blue),
|
||||
color!(Color::Red),
|
||||
],
|
||||
rx_style: color!(FIRST_COLOUR),
|
||||
tx_style: color!(SECOND_COLOUR),
|
||||
total_rx_style: color!(THIRD_COLOUR),
|
||||
total_tx_style: color!(FOURTH_COLOUR),
|
||||
all_cpu_colour: color!(ALL_COLOUR),
|
||||
avg_cpu_colour: color!(AVG_COLOUR),
|
||||
cpu_colour_styles: vec![
|
||||
color!(Color::LightMagenta),
|
||||
color!(Color::LightYellow),
|
||||
color!(Color::LightCyan),
|
||||
color!(Color::LightGreen),
|
||||
color!(Color::LightBlue),
|
||||
color!(Color::Cyan),
|
||||
color!(Color::Green),
|
||||
color!(Color::Blue),
|
||||
],
|
||||
border_style: color!(TEXT_COLOUR),
|
||||
highlighted_border_style: color!(HIGHLIGHT_COLOUR),
|
||||
text_style: color!(TEXT_COLOUR),
|
||||
widget_title_style: color!(TEXT_COLOUR),
|
||||
graph_style: color!(TEXT_COLOUR),
|
||||
graph_legend_style: color!(TEXT_COLOUR),
|
||||
high_battery: color!(Color::Green),
|
||||
medium_battery: color!(Color::Yellow),
|
||||
low_battery: color!(Color::Red),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: color!(Color::DarkGray),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_light_mode() -> Self {
|
||||
Self {
|
||||
selected_text_style: color!(Color::White),
|
||||
table_header_style: color!(Color::Black),
|
||||
ram_style: color!(Color::Blue),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: color!(Color::LightRed),
|
||||
swap_style: color!(Color::Red),
|
||||
arc_style: color!(Color::LightBlue),
|
||||
gpu_colours: vec![
|
||||
color!(Color::LightGreen),
|
||||
color!(Color::LightCyan),
|
||||
color!(Color::LightRed),
|
||||
color!(Color::Cyan),
|
||||
color!(Color::Green),
|
||||
color!(Color::Blue),
|
||||
color!(Color::Red),
|
||||
],
|
||||
rx_style: color!(Color::Blue),
|
||||
tx_style: color!(Color::Red),
|
||||
total_rx_style: color!(Color::LightBlue),
|
||||
total_tx_style: color!(Color::LightRed),
|
||||
cpu_colour_styles: vec![
|
||||
color!(Color::LightMagenta),
|
||||
color!(Color::LightBlue),
|
||||
color!(Color::LightRed),
|
||||
color!(Color::Cyan),
|
||||
color!(Color::Green),
|
||||
color!(Color::Blue),
|
||||
color!(Color::Red),
|
||||
],
|
||||
border_style: color!(Color::Black),
|
||||
text_style: color!(Color::Black),
|
||||
widget_title_style: color!(Color::Black),
|
||||
graph_style: color!(Color::Black),
|
||||
graph_legend_style: color!(Color::Black),
|
||||
disabled_text_style: color!(Color::Gray),
|
||||
..Self::default_palette()
|
||||
}
|
||||
}
|
||||
}
|
127
src/options/config/style/themes/gruvbox.rs
Normal file
127
src/options/config/style/themes/gruvbox.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use tui::style::Color;
|
||||
|
||||
use crate::options::config::style::{utils::convert_hex_to_color, ColourPalette};
|
||||
|
||||
use super::{color, hex};
|
||||
|
||||
impl ColourPalette {
|
||||
pub(crate) fn gruvbox_palette() -> Self {
|
||||
Self {
|
||||
selected_text_style: hex!("#1d2021").bg(convert_hex_to_color("#ebdbb2").unwrap()),
|
||||
table_header_style: hex!("#83a598"),
|
||||
ram_style: hex!("#8ec07c"),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#b16286"),
|
||||
swap_style: hex!("#fabd2f"),
|
||||
arc_style: hex!("#689d6a"),
|
||||
gpu_colours: vec![
|
||||
hex!("#d79921"),
|
||||
hex!("#458588"),
|
||||
hex!("#b16286"),
|
||||
hex!("#fe8019"),
|
||||
hex!("#b8bb26"),
|
||||
hex!("#cc241d"),
|
||||
hex!("#98971a"),
|
||||
],
|
||||
rx_style: hex!("#8ec07c"),
|
||||
tx_style: hex!("#fabd2f"),
|
||||
total_rx_style: hex!("#689d6a"),
|
||||
total_tx_style: hex!("#d79921"),
|
||||
all_cpu_colour: hex!("#8ec07c"),
|
||||
avg_cpu_colour: hex!("#fb4934"),
|
||||
cpu_colour_styles: vec![
|
||||
hex!("#cc241d"),
|
||||
hex!("#98971a"),
|
||||
hex!("#d79921"),
|
||||
hex!("#458588"),
|
||||
hex!("#b16286"),
|
||||
hex!("#689d6a"),
|
||||
hex!("#fe8019"),
|
||||
hex!("#b8bb26"),
|
||||
hex!("#fabd2f"),
|
||||
hex!("#83a598"),
|
||||
hex!("#d3869b"),
|
||||
hex!("#d65d0e"),
|
||||
hex!("#9d0006"),
|
||||
hex!("#79740e"),
|
||||
hex!("#b57614"),
|
||||
hex!("#076678"),
|
||||
hex!("#8f3f71"),
|
||||
hex!("#427b58"),
|
||||
hex!("#d65d03"),
|
||||
hex!("#af3a03"),
|
||||
],
|
||||
border_style: hex!("#ebdbb2"),
|
||||
highlighted_border_style: hex!("#fe8019"),
|
||||
text_style: hex!("#ebdbb2"),
|
||||
widget_title_style: hex!("#ebdbb2"),
|
||||
graph_style: hex!("#ebdbb2"),
|
||||
graph_legend_style: hex!("#ebdbb2"),
|
||||
high_battery: hex!("#98971a"),
|
||||
medium_battery: hex!("#fabd2f"),
|
||||
low_battery: hex!("#fb4934"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#665c54"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gruvbox_light_palette() -> Self {
|
||||
Self {
|
||||
selected_text_style: hex!("#ebdbb2").bg(convert_hex_to_color("#3c3836").unwrap()),
|
||||
table_header_style: hex!("#076678"),
|
||||
ram_style: hex!("#427b58"),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#d79921"),
|
||||
swap_style: hex!("#cc241d"),
|
||||
arc_style: hex!("#689d6a"),
|
||||
gpu_colours: vec![
|
||||
hex!("#9d0006"),
|
||||
hex!("#98971a"),
|
||||
hex!("#d79921"),
|
||||
hex!("#458588"),
|
||||
hex!("#b16286"),
|
||||
hex!("#fe8019"),
|
||||
hex!("#b8bb26"),
|
||||
],
|
||||
rx_style: hex!("#427b58"),
|
||||
tx_style: hex!("#cc241d"),
|
||||
total_rx_style: hex!("#689d6a"),
|
||||
total_tx_style: hex!("#d79921"),
|
||||
all_cpu_colour: hex!("#8ec07c"),
|
||||
avg_cpu_colour: hex!("#fb4934"),
|
||||
cpu_colour_styles: vec![
|
||||
hex!("#cc241d"),
|
||||
hex!("#98971a"),
|
||||
hex!("#d79921"),
|
||||
hex!("#458588"),
|
||||
hex!("#b16286"),
|
||||
hex!("#689d6a"),
|
||||
hex!("#fe8019"),
|
||||
hex!("#b8bb26"),
|
||||
hex!("#fabd2f"),
|
||||
hex!("#83a598"),
|
||||
hex!("#d3869b"),
|
||||
hex!("#d65d0e"),
|
||||
hex!("#9d0006"),
|
||||
hex!("#79740e"),
|
||||
hex!("#b57614"),
|
||||
hex!("#076678"),
|
||||
hex!("#8f3f71"),
|
||||
hex!("#427b58"),
|
||||
hex!("#d65d03"),
|
||||
hex!("#af3a03"),
|
||||
],
|
||||
border_style: hex!("#ebdbb2"),
|
||||
highlighted_border_style: hex!("#fe8019"),
|
||||
text_style: hex!("#ebdbb2"),
|
||||
widget_title_style: hex!("#ebdbb2"),
|
||||
graph_style: hex!("#ebdbb2"),
|
||||
graph_legend_style: hex!("#ebdbb2"),
|
||||
high_battery: hex!("#98971a"),
|
||||
medium_battery: hex!("#fabd2f"),
|
||||
low_battery: hex!("#fb4934"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#665c54"),
|
||||
}
|
||||
}
|
||||
}
|
103
src/options/config/style/themes/nord.rs
Normal file
103
src/options/config/style/themes/nord.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use tui::style::Color;
|
||||
|
||||
use crate::options::config::style::{utils::convert_hex_to_color, ColourPalette};
|
||||
|
||||
use super::{color, hex};
|
||||
|
||||
impl ColourPalette {
|
||||
pub(crate) fn nord_palette() -> Self {
|
||||
Self {
|
||||
selected_text_style: hex!("#2e3440").bg(convert_hex_to_color("#88c0d0").unwrap()),
|
||||
table_header_style: hex!("#81a1c1"),
|
||||
ram_style: hex!("#88c0d0"),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#d8dee9"),
|
||||
swap_style: hex!("#d08770"),
|
||||
arc_style: hex!("#5e81ac"),
|
||||
gpu_colours: vec![
|
||||
hex!("#8fbcbb"),
|
||||
hex!("#81a1c1"),
|
||||
hex!("#d8dee9"),
|
||||
hex!("#b48ead"),
|
||||
hex!("#a3be8c"),
|
||||
hex!("#ebcb8b"),
|
||||
hex!("#bf616a"),
|
||||
],
|
||||
rx_style: hex!("#88c0d0"),
|
||||
tx_style: hex!("#d08770"),
|
||||
total_rx_style: hex!("#5e81ac"),
|
||||
total_tx_style: hex!("#8fbcbb"),
|
||||
all_cpu_colour: hex!("#88c0d0"),
|
||||
avg_cpu_colour: hex!("#8fbcbb"),
|
||||
cpu_colour_styles: vec![
|
||||
hex!("#5e81ac"),
|
||||
hex!("#81a1c1"),
|
||||
hex!("#d8dee9"),
|
||||
hex!("#b48ead"),
|
||||
hex!("#a3be8c"),
|
||||
hex!("#ebcb8b"),
|
||||
hex!("#d08770"),
|
||||
hex!("#bf616a"),
|
||||
],
|
||||
border_style: hex!("#88c0d0"),
|
||||
highlighted_border_style: hex!("#5e81ac"),
|
||||
text_style: hex!("#e5e9f0"),
|
||||
widget_title_style: hex!("#e5e9f0"),
|
||||
graph_style: hex!("#e5e9f0"),
|
||||
graph_legend_style: hex!("#e5e9f0"),
|
||||
high_battery: hex!("#a3be8c"),
|
||||
medium_battery: hex!("#ebcb8b"),
|
||||
low_battery: hex!("#bf616a"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#4c566a"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn nord_light_palette() -> Self {
|
||||
Self {
|
||||
selected_text_style: hex!("#f5f5f5").bg(convert_hex_to_color("#5e81ac").unwrap()),
|
||||
table_header_style: hex!("#5e81ac"),
|
||||
ram_style: hex!("#81a1c1"),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#4c566a"),
|
||||
swap_style: hex!("#d08770"),
|
||||
arc_style: hex!("#5e81ac"),
|
||||
gpu_colours: vec![
|
||||
hex!("#8fbcbb"),
|
||||
hex!("#88c0d0"),
|
||||
hex!("#4c566a"),
|
||||
hex!("#b48ead"),
|
||||
hex!("#a3be8c"),
|
||||
hex!("#ebcb8b"),
|
||||
hex!("#bf616a"),
|
||||
],
|
||||
rx_style: hex!("#81a1c1"),
|
||||
tx_style: hex!("#d08770"),
|
||||
total_rx_style: hex!("#5e81ac"),
|
||||
total_tx_style: hex!("#8fbcbb"),
|
||||
all_cpu_colour: hex!("#81a1c1"),
|
||||
avg_cpu_colour: hex!("#8fbcbb"),
|
||||
cpu_colour_styles: vec![
|
||||
hex!("#5e81ac"),
|
||||
hex!("#88c0d0"),
|
||||
hex!("#4c566a"),
|
||||
hex!("#b48ead"),
|
||||
hex!("#a3be8c"),
|
||||
hex!("#ebcb8b"),
|
||||
hex!("#d08770"),
|
||||
hex!("#bf616a"),
|
||||
],
|
||||
border_style: hex!("#2e3440"),
|
||||
highlighted_border_style: hex!("#5e81ac"),
|
||||
text_style: hex!("#2e3440"),
|
||||
widget_title_style: hex!("#2e3440"),
|
||||
graph_style: hex!("#2e3440"),
|
||||
graph_legend_style: hex!("#2e3440"),
|
||||
high_battery: hex!("#a3be8c"),
|
||||
medium_battery: hex!("#ebcb8b"),
|
||||
low_battery: hex!("#bf616a"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#d8dee9"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,9 @@
|
|||
use concat_string::concat_string;
|
||||
use itertools::Itertools;
|
||||
use tui::style::{Color, Style};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
pub const FIRST_COLOUR: Color = Color::LightMagenta;
|
||||
pub const SECOND_COLOUR: Color = Color::LightYellow;
|
||||
pub const THIRD_COLOUR: Color = Color::LightCyan;
|
||||
pub const FOURTH_COLOUR: Color = Color::LightGreen;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub const FIFTH_COLOUR: Color = Color::LightRed;
|
||||
pub const HIGHLIGHT_COLOUR: Color = Color::LightBlue;
|
||||
pub const AVG_COLOUR: Color = Color::Red;
|
||||
pub const ALL_COLOUR: Color = Color::Green;
|
||||
|
||||
/// Convert a hex string to a colour.
|
||||
fn convert_hex_to_color(hex: &str) -> Result<Color, String> {
|
||||
pub(super) fn convert_hex_to_color(hex: &str) -> Result<Color, String> {
|
||||
fn hex_component_to_int(hex: &str, first: &str, second: &str) -> Result<u8, String> {
|
||||
u8::from_str_radix(&concat_string!(first, second), 16)
|
||||
.map_err(|_| format!("'{hex}' is an invalid hex color, could not decode."))
|
||||
|
@ -30,7 +19,7 @@ fn convert_hex_to_color(hex: &str) -> Result<Color, String> {
|
|||
return Err(invalid_hex_format(hex));
|
||||
}
|
||||
|
||||
let components = hex.graphemes(true).collect_vec();
|
||||
let components: Vec<&str> = hex.graphemes(true).collect();
|
||||
if components.len() == 7 {
|
||||
// A 6-long hex.
|
||||
let r = hex_component_to_int(hex, components[1], components[2])?;
|
||||
|
@ -50,10 +39,6 @@ fn convert_hex_to_color(hex: &str) -> Result<Color, String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn str_to_fg(input_val: &str) -> Result<Style, String> {
|
||||
Ok(Style::default().fg(str_to_colour(input_val)?))
|
||||
}
|
||||
|
||||
pub fn str_to_colour(input_val: &str) -> Result<Color, String> {
|
||||
if input_val.len() > 1 {
|
||||
if input_val.starts_with('#') {
|
||||
|
@ -68,6 +53,10 @@ pub fn str_to_colour(input_val: &str) -> Result<Color, String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn str_to_fg(input_val: &str) -> Result<Style, String> {
|
||||
Ok(Style::default().fg(str_to_colour(input_val)?))
|
||||
}
|
||||
|
||||
fn convert_rgb_to_color(rgb_str: &str) -> Result<Color, String> {
|
||||
let rgb_list = rgb_str.split(',').collect::<Vec<&str>>();
|
||||
if rgb_list.len() != 3 {
|
||||
|
@ -118,7 +107,7 @@ fn convert_name_to_colour(color_name: &str) -> Result<Color, String> {
|
|||
_ => Err(format!(
|
||||
"'{color_name}' is an invalid named color.
|
||||
|
||||
The following are supported strings:
|
||||
The following are supported named colors:
|
||||
+--------+-------------+---------------------+
|
||||
| Reset | Magenta | Light Yellow |
|
||||
+--------+-------------+---------------------+
|
||||
|
@ -131,13 +120,85 @@ The following are supported strings:
|
|||
| Yellow | Light Red | White |
|
||||
+--------+-------------+---------------------+
|
||||
| Blue | Light Green | |
|
||||
+--------+-------------+---------------------+\n"
|
||||
+--------+-------------+---------------------+
|
||||
|
||||
Alternatively, hex colors or RGB color codes are valid.\n"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! opt {
|
||||
($($e: tt)+) => {
|
||||
(|| { $($e)+ })()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! set_style {
|
||||
($palette_field:expr, $config_location:expr, $field:tt) => {
|
||||
if let Some(style) = &(opt!($config_location.as_ref()?.$field.as_ref())) {
|
||||
if let Some(colour) = &style.color {
|
||||
$palette_field = crate::options::config::style::utils::str_to_fg(&colour.0)
|
||||
.map_err(|err| match stringify!($config_location).split_once(".") {
|
||||
Some((_, loc)) => OptionError::config(format!(
|
||||
"Please update 'styles.{loc}.{}' in your config file. {err}",
|
||||
stringify!($field)
|
||||
)),
|
||||
None => OptionError::config(format!(
|
||||
"Please update 'styles.{}' in your config file. {err}",
|
||||
stringify!($field)
|
||||
)),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! set_colour {
|
||||
($palette_field:expr, $config_location:expr, $field:tt) => {
|
||||
if let Some(colour) = &(opt!($config_location.as_ref()?.$field.as_ref())) {
|
||||
$palette_field =
|
||||
crate::options::config::style::utils::str_to_fg(&colour.0).map_err(|err| {
|
||||
match stringify!($config_location).split_once(".") {
|
||||
Some((_, loc)) => OptionError::config(format!(
|
||||
"Please update 'styles.{loc}.{}' in your config file. {err}",
|
||||
stringify!($field)
|
||||
)),
|
||||
None => OptionError::config(format!(
|
||||
"Please update 'styles.{}' in your config file. {err}",
|
||||
stringify!($field)
|
||||
)),
|
||||
}
|
||||
})?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! set_colour_list {
|
||||
($palette_field:expr, $config_location:expr, $field:tt) => {
|
||||
if let Some(colour_list) = &(opt!($config_location.as_ref()?.$field.as_ref())) {
|
||||
$palette_field = colour_list
|
||||
.iter()
|
||||
.map(|s| crate::options::config::style::utils::str_to_fg(&s.0))
|
||||
.collect::<Result<Vec<Style>, String>>()
|
||||
.map_err(|err| match stringify!($config_location).split_once(".") {
|
||||
Some((_, loc)) => OptionError::config(format!(
|
||||
"Please update 'styles.{loc}.{}' in your config file. {err}",
|
||||
stringify!($field)
|
||||
)),
|
||||
None => OptionError::config(format!(
|
||||
"Please update 'styles.{}' in your config file. {err}",
|
||||
stringify!($field)
|
||||
)),
|
||||
})?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) use {opt, set_colour, set_colour_list, set_style};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
16
src/options/config/style/widgets.rs
Normal file
16
src/options/config/style/widgets.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ColorStr, TextStyleConfig};
|
||||
|
||||
/// General styling for generic widgets.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
pub(crate) struct WidgetStyle {
|
||||
pub(crate) border: Option<ColorStr>,
|
||||
pub(crate) selected_border: Option<ColorStr>,
|
||||
pub(crate) widget_title: Option<TextStyleConfig>,
|
||||
|
||||
pub(crate) text: Option<TextStyleConfig>,
|
||||
pub(crate) selected_text: Option<TextStyleConfig>,
|
||||
pub(crate) disabled_text: Option<TextStyleConfig>,
|
||||
}
|
|
@ -10,12 +10,11 @@ use crate::{
|
|||
Column, ColumnHeader, DataTable, DataTableColumn, DataTableProps, DataTableStyling,
|
||||
DataToCell,
|
||||
},
|
||||
styling::CanvasStyling,
|
||||
Painter,
|
||||
},
|
||||
data_collection::cpu::CpuDataType,
|
||||
data_conversion::CpuWidgetData,
|
||||
options::config::cpu::CpuDefault,
|
||||
options::config::{cpu::CpuDefault, style::ColourPalette},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -26,16 +25,16 @@ pub struct CpuWidgetStyling {
|
|||
}
|
||||
|
||||
impl CpuWidgetStyling {
|
||||
fn from_colours(colours: &CanvasStyling) -> Self {
|
||||
let entries = if colours.cpu_colour_styles.is_empty() {
|
||||
fn from_colours(palette: &ColourPalette) -> Self {
|
||||
let entries = if palette.cpu_colour_styles.is_empty() {
|
||||
vec![Style::default()]
|
||||
} else {
|
||||
colours.cpu_colour_styles.clone()
|
||||
palette.cpu_colour_styles.clone()
|
||||
};
|
||||
|
||||
Self {
|
||||
all: colours.all_colour_style,
|
||||
avg: colours.avg_colour_style,
|
||||
all: palette.all_cpu_colour,
|
||||
avg: palette.avg_cpu_colour,
|
||||
entries,
|
||||
}
|
||||
}
|
||||
|
@ -130,12 +129,12 @@ impl DataToCell<CpuWidgetColumn> for CpuWidgetTableData {
|
|||
#[inline(always)]
|
||||
fn style_row<'a>(&self, row: Row<'a>, painter: &Painter) -> Row<'a> {
|
||||
let style = match self {
|
||||
CpuWidgetTableData::All => painter.colours.all_colour_style,
|
||||
CpuWidgetTableData::All => painter.colours.all_cpu_colour,
|
||||
CpuWidgetTableData::Entry {
|
||||
data_type,
|
||||
last_entry: _,
|
||||
} => match data_type {
|
||||
CpuDataType::Avg => painter.colours.avg_colour_style,
|
||||
CpuDataType::Avg => painter.colours.avg_cpu_colour,
|
||||
CpuDataType::Cpu(index) => {
|
||||
painter.colours.cpu_colour_styles
|
||||
[index % painter.colours.cpu_colour_styles.len()]
|
||||
|
@ -168,7 +167,7 @@ pub struct CpuWidgetState {
|
|||
impl CpuWidgetState {
|
||||
pub fn new(
|
||||
config: &AppConfigFields, default_selection: CpuDefault, current_display_time: u64,
|
||||
autohide_timer: Option<Instant>, colours: &CanvasStyling,
|
||||
autohide_timer: Option<Instant>, colours: &ColourPalette,
|
||||
) -> Self {
|
||||
const COLUMNS: [Column<CpuWidgetColumn>; 2] = [
|
||||
Column::soft(CpuWidgetColumn::CPU, Some(0.5)),
|
||||
|
@ -184,7 +183,7 @@ impl CpuWidgetState {
|
|||
show_current_entry_when_unfocused: true,
|
||||
};
|
||||
|
||||
let styling = DataTableStyling::from_colours(colours);
|
||||
let styling = DataTableStyling::from_palette(colours);
|
||||
let mut table = DataTable::new(COLUMNS, props, styling);
|
||||
match default_selection {
|
||||
CpuDefault::All => {}
|
||||
|
|
|
@ -2,13 +2,11 @@ use std::{borrow::Cow, cmp::max, num::NonZeroU16};
|
|||
|
||||
use crate::{
|
||||
app::AppConfigFields,
|
||||
canvas::{
|
||||
components::data_table::{
|
||||
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell,
|
||||
SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
styling::CanvasStyling,
|
||||
canvas::components::data_table::{
|
||||
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell, SortColumn,
|
||||
SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
options::config::style::ColourPalette,
|
||||
utils::{data_prefixes::get_decimal_bytes, general::sort_partial_fn},
|
||||
};
|
||||
|
||||
|
@ -206,7 +204,7 @@ impl SortsRow for DiskWidgetColumn {
|
|||
}
|
||||
|
||||
impl DiskTableWidget {
|
||||
pub fn new(config: &AppConfigFields, colours: &CanvasStyling) -> Self {
|
||||
pub fn new(config: &AppConfigFields, palette: &ColourPalette) -> Self {
|
||||
let columns = [
|
||||
SortColumn::soft(DiskWidgetColumn::Disk, Some(0.2)),
|
||||
SortColumn::soft(DiskWidgetColumn::Mount, Some(0.2)),
|
||||
|
@ -231,7 +229,7 @@ impl DiskTableWidget {
|
|||
order: SortOrder::Ascending,
|
||||
};
|
||||
|
||||
let styling = DataTableStyling::from_colours(colours);
|
||||
let styling = DataTableStyling::from_palette(palette);
|
||||
|
||||
Self {
|
||||
table: SortDataTable::new_sortable(columns, props, styling),
|
||||
|
|
|
@ -17,14 +17,12 @@ use crate::{
|
|||
query::*,
|
||||
AppConfigFields, AppSearchState,
|
||||
},
|
||||
canvas::{
|
||||
components::data_table::{
|
||||
Column, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataTableProps,
|
||||
DataTableStyling, SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
styling::CanvasStyling,
|
||||
canvas::components::data_table::{
|
||||
Column, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataTableProps,
|
||||
DataTableStyling, SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
data_collection::processes::{Pid, ProcessHarvest},
|
||||
options::config::style::ColourPalette,
|
||||
};
|
||||
|
||||
/// ProcessSearchState only deals with process' search's current settings and
|
||||
|
@ -161,7 +159,7 @@ pub struct ProcWidgetState {
|
|||
}
|
||||
|
||||
impl ProcWidgetState {
|
||||
fn new_sort_table(config: &AppConfigFields, colours: &CanvasStyling) -> SortTable {
|
||||
fn new_sort_table(config: &AppConfigFields, palette: &ColourPalette) -> SortTable {
|
||||
const COLUMNS: [Column<SortTableColumn>; 1] = [Column::hard(SortTableColumn, 7)];
|
||||
|
||||
let props = DataTableProps {
|
||||
|
@ -172,13 +170,13 @@ impl ProcWidgetState {
|
|||
show_table_scroll_position: false,
|
||||
show_current_entry_when_unfocused: false,
|
||||
};
|
||||
let styling = DataTableStyling::from_colours(colours);
|
||||
let styling = DataTableStyling::from_palette(palette);
|
||||
|
||||
DataTable::new(COLUMNS, props, styling)
|
||||
}
|
||||
|
||||
fn new_process_table(
|
||||
config: &AppConfigFields, colours: &CanvasStyling, columns: Vec<SortColumn<ProcColumn>>,
|
||||
config: &AppConfigFields, colours: &ColourPalette, columns: Vec<SortColumn<ProcColumn>>,
|
||||
default_index: usize, default_order: SortOrder,
|
||||
) -> ProcessTable {
|
||||
let inner_props = DataTableProps {
|
||||
|
@ -194,14 +192,14 @@ impl ProcWidgetState {
|
|||
sort_index: default_index,
|
||||
order: default_order,
|
||||
};
|
||||
let styling = DataTableStyling::from_colours(colours);
|
||||
let styling = DataTableStyling::from_palette(colours);
|
||||
|
||||
DataTable::new_sortable(columns, props, styling)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
config: &AppConfigFields, mode: ProcWidgetMode, table_config: ProcTableConfig,
|
||||
colours: &CanvasStyling, config_columns: &Option<IndexSet<ProcWidgetColumn>>,
|
||||
colours: &ColourPalette, config_columns: &Option<IndexSet<ProcWidgetColumn>>,
|
||||
) -> Self {
|
||||
let process_search_state = {
|
||||
let mut pss = ProcessSearchState::default();
|
||||
|
@ -1142,7 +1140,7 @@ mod test {
|
|||
|
||||
fn init_state(table_config: ProcTableConfig, columns: &[ProcWidgetColumn]) -> ProcWidgetState {
|
||||
let config = AppConfigFields::default();
|
||||
let styling = CanvasStyling::default();
|
||||
let styling = ColourPalette::default();
|
||||
let columns = Some(columns.iter().cloned().collect());
|
||||
|
||||
ProcWidgetState::new(
|
||||
|
|
|
@ -4,14 +4,12 @@ use concat_string::concat_string;
|
|||
|
||||
use crate::{
|
||||
app::AppConfigFields,
|
||||
canvas::{
|
||||
components::data_table::{
|
||||
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell,
|
||||
SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
styling::CanvasStyling,
|
||||
canvas::components::data_table::{
|
||||
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell, SortColumn,
|
||||
SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
data_collection::temperature::TemperatureType,
|
||||
options::config::style::ColourPalette,
|
||||
utils::general::sort_partial_fn,
|
||||
};
|
||||
|
||||
|
@ -102,7 +100,7 @@ pub struct TempWidgetState {
|
|||
}
|
||||
|
||||
impl TempWidgetState {
|
||||
pub fn new(config: &AppConfigFields, colours: &CanvasStyling) -> Self {
|
||||
pub(crate) fn new(config: &AppConfigFields, palette: &ColourPalette) -> Self {
|
||||
let columns = [
|
||||
SortColumn::soft(TempWidgetColumn::Sensor, Some(0.8)),
|
||||
SortColumn::soft(TempWidgetColumn::Temp, None).default_descending(),
|
||||
|
@ -121,7 +119,7 @@ impl TempWidgetState {
|
|||
order: SortOrder::Ascending,
|
||||
};
|
||||
|
||||
let styling = DataTableStyling::from_colours(colours);
|
||||
let styling = DataTableStyling::from_palette(palette);
|
||||
|
||||
Self {
|
||||
table: SortDataTable::new_sortable(columns, props, styling),
|
||||
|
|
|
@ -67,3 +67,18 @@ fn test_all_proc() {
|
|||
fn test_cpu_doughnut() {
|
||||
run_and_kill(&["-C", "./tests/valid_configs/cpu_doughnut.toml"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_theme() {
|
||||
run_and_kill(&["-C", "./tests/valid_configs/theme.toml"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_styling_sanity_check() {
|
||||
run_and_kill(&["-C", "./tests/valid_configs/styling.toml"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_styling_sanity_check_2() {
|
||||
run_and_kill(&["-C", "./tests/valid_configs/styling_2.toml"]);
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[colors]
|
||||
table_header_color="#zzzzzz"
|
||||
[styles.tables.headers]
|
||||
color="#zzzzzz"
|
|
@ -1,2 +1,2 @@
|
|||
[colors]
|
||||
table_header_color="#1111111"
|
||||
[styles.tables.headers]
|
||||
color="#1111111"
|
|
@ -1,2 +1,2 @@
|
|||
[colors]
|
||||
table_header_color = "#加拿大"
|
||||
[styles.tables.headers]
|
||||
color = "#加拿大"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[colors]
|
||||
table_header_color = "LightB lue"
|
||||
[styles.tables.headers]
|
||||
color = "LightB lue"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[colors]
|
||||
table_header_color="257, 50, 50"
|
||||
[styles.tables.headers]
|
||||
color="257, 50, 50"
|
|
@ -1,2 +1,2 @@
|
|||
[colors]
|
||||
table_header_color="50, 50, 50, 50"
|
||||
[styles.tables.headers]
|
||||
color="50, 50, 50, 50"
|
|
@ -1,2 +1,2 @@
|
|||
[colors]
|
||||
table_header_color="this is not a colour"
|
||||
[styles.tables.headers]
|
||||
color="this is not a colour"
|
13
tests/valid_configs/styling.toml
Normal file
13
tests/valid_configs/styling.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Test basic colours
|
||||
[styles.cpu]
|
||||
all_entry_color="255, 50, 50"
|
||||
|
||||
# Test tables
|
||||
[styles.graphs.legend_text]
|
||||
color = "#fff"
|
||||
bg_color = "#000"
|
||||
bold = false
|
||||
|
||||
# Test inline tables
|
||||
[styles.tables]
|
||||
headers = { color = "red", bg_color = "reset", bold = true }
|
53
tests/valid_configs/styling_2.toml
Normal file
53
tests/valid_configs/styling_2.toml
Normal file
|
@ -0,0 +1,53 @@
|
|||
# These are all the components that support custom theming. Note that colour support
|
||||
# will depend on terminal support.
|
||||
[styles]
|
||||
|
||||
# Built-in themes. Valid values are:
|
||||
# - "default"
|
||||
# - "default-light"
|
||||
# - "gruvbox"
|
||||
# - "gruvbox-light"
|
||||
# - "nord"
|
||||
# - "nord-light".
|
||||
|
||||
# This will have the lowest precedence if a custom colour palette is set,
|
||||
# or overriden if the command-line flag for a built-in theme is set.
|
||||
theme = "default"
|
||||
|
||||
[styles.cpu]
|
||||
all_entry_color = "green"
|
||||
avg_entry_color = "red"
|
||||
cpu_core_colors = ["light magenta", "light yellow", "light cyan", "light green", "light blue", "cyan", "green", "blue"]
|
||||
|
||||
[styles.memory]
|
||||
ram = "light magenta"
|
||||
cache = "light red"
|
||||
swap = "light yellow"
|
||||
arc = "light cyan"
|
||||
gpus = ["light blue", "light red", "cyan", "green", "blue", "red"]
|
||||
|
||||
[styles.network]
|
||||
rx = "light magenta"
|
||||
tx = "light yellow"
|
||||
rx_total = "light cyan"
|
||||
tx_total = "light green"
|
||||
|
||||
[styles.battery]
|
||||
high_battery = "green"
|
||||
medium_battery = "yellow"
|
||||
low_battery = "red"
|
||||
|
||||
[styles.tables]
|
||||
headers = {color = "light blue"}
|
||||
|
||||
[styles.graphs]
|
||||
graph_color = "gray"
|
||||
legend_text = {color = "gray"}
|
||||
|
||||
[styles.widgets]
|
||||
border = "gray"
|
||||
selected_border = "light blue"
|
||||
widget_title = {color = "gray"}
|
||||
text = {color = "gray"}
|
||||
selected_text = {color = "black", bg_color = "light blue"}
|
||||
disabled_text = {color = "dark gray"}
|
4
tests/valid_configs/theme.toml
Normal file
4
tests/valid_configs/theme.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
#:schema none
|
||||
|
||||
[styles]
|
||||
theme = "gruvbox"
|
Loading…
Reference in a new issue