CI/CD & Formatting (#6)
* Improve autoformatting guides, add editorconfig and add CI/CD
This commit is contained in:
parent
4d6b38f3e1
commit
c57713ca9a
17 changed files with 209 additions and 98 deletions
8
.editorconfig
Normal file
8
.editorconfig
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
26
.github/workflows/build.yml
vendored
Normal file
26
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Format
|
||||||
|
run: cargo build
|
26
.github/workflows/fmt.yml
vendored
Normal file
26
.github/workflows/fmt.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: format
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Format
|
||||||
|
run: cargo fmt -- --check
|
26
.github/workflows/test.yml
vendored
Normal file
26
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Test
|
||||||
|
run: cargo test --verbose
|
|
@ -4,8 +4,6 @@ version = "0.1.0"
|
||||||
authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"]
|
authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gtk = { version = "0.9", features = [ "v3_16" ] }
|
gtk = { version = "0.9", features = [ "v3_16" ] }
|
||||||
gdk = { version = "", features = ["v3_16"] }
|
gdk = { version = "", features = ["v3_16"] }
|
||||||
|
@ -13,7 +11,6 @@ gio = { version = "", features = ["v2_44"] }
|
||||||
glib = { version = "", features = ["v2_44"] }
|
glib = { version = "", features = ["v2_44"] }
|
||||||
gdk-pixbuf = "0.9"
|
gdk-pixbuf = "0.9"
|
||||||
|
|
||||||
|
|
||||||
regex = "1"
|
regex = "1"
|
||||||
try_match = "0.2.2"
|
try_match = "0.2.2"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
@ -39,8 +36,5 @@ lazy_static = "1.4.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
ref-cast = "1.0"
|
ref-cast = "1.0"
|
||||||
|
|
||||||
#thiserror = "1.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.6.1"
|
pretty_assertions = "0.6.1"
|
||||||
|
|
||||||
|
|
27
README.md
27
README.md
|
@ -1,15 +1,12 @@
|
||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
|
|
||||||
* [About](#org4ab08b6)
|
- [About](#org4ab08b6)
|
||||||
* [Configuration](#org581ca61)
|
- [Configuration](#org581ca61)
|
||||||
* [Example Config](#orgb769597)
|
- [Example Config](#orgb769597)
|
||||||
* [Building](#orgbf66ce2)
|
- [Building](#orgbf66ce2)
|
||||||
* [Prerequisites](#org727b3da)
|
- [Prerequisites](#org727b3da)
|
||||||
* [Installation](#orgdd31739)
|
- [Installation](#orgdd31739)
|
||||||
* [Usage](#org4a9b3c6)
|
- [Usage](#org4a9b3c6)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a id="org4ab08b6"></a>
|
<a id="org4ab08b6"></a>
|
||||||
|
|
||||||
|
@ -17,17 +14,16 @@
|
||||||
|
|
||||||
Elkowar’s Wacky Widgets is a standalone Widget System made in rust to add AwesomeWM like widgets to any WM
|
Elkowar’s Wacky Widgets is a standalone Widget System made in rust to add AwesomeWM like widgets to any WM
|
||||||
|
|
||||||
|
|
||||||
<a id="org581ca61"></a>
|
<a id="org581ca61"></a>
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
Eww’s configuration should be placed in `~/.config/eww/eww.xml` and any `scss` styles you want to add should be put into `~/.config/eww/eww.scss`.
|
Eww’s configuration should be placed in `~/.config/eww/eww.xml` and any `scss` styles you want to add should be put into `~/.config/eww/eww.scss`.
|
||||||
|
|
||||||
|
|
||||||
<a id="orgb769597"></a>
|
<a id="orgb769597"></a>
|
||||||
|
|
||||||
## Example Config
|
## Example Config
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<eww>
|
<eww>
|
||||||
<definitions>
|
<definitions>
|
||||||
|
@ -66,17 +62,16 @@ Eww’s configuration should be placed in `~/.config/eww/eww.xml` and any `s
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
|
|
||||||
<a id="org727b3da"></a>
|
<a id="org727b3da"></a>
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- cargo with nightly toolchain
|
- rustc
|
||||||
|
- cargo (nightly toolchain)
|
||||||
|
|
||||||
Rather than with your system package manager, I recommend installing it using [rustup](https://rustup.rs/),
|
Rather than with your system package manager, I recommend installing it using [rustup](https://rustup.rs/),
|
||||||
as this makes it easy to use the nightly toolchain, which is necessary to build eww.
|
as this makes it easy to use the nightly toolchain, which is necessary to build eww.
|
||||||
|
|
||||||
|
|
||||||
<a id="orgdd31739"></a>
|
<a id="orgdd31739"></a>
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -89,10 +84,8 @@ Build the Binary using -:
|
||||||
|
|
||||||
then copy the built binary from `./target/release` to anywhere in `$PATH` (example - `~/.local/bin`)
|
then copy the built binary from `./target/release` to anywhere in `$PATH` (example - `~/.local/bin`)
|
||||||
|
|
||||||
|
|
||||||
<a id="org4a9b3c6"></a>
|
<a id="org4a9b3c6"></a>
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
Create a Config and then just do `eww`!
|
Create a Config and then just do `eww`!
|
||||||
|
|
||||||
|
|
17
rustfmt.toml
17
rustfmt.toml
|
@ -1,4 +1,13 @@
|
||||||
unstable_features=true
|
unstable_features = true
|
||||||
fn_single_line=false
|
fn_single_line = false
|
||||||
max_width=130
|
max_width = 130
|
||||||
|
reorder_impl_items = true
|
||||||
|
merge_imports = true
|
||||||
|
normalize_comments = true
|
||||||
|
use_field_init_shorthand = true
|
||||||
|
wrap_comments = true
|
||||||
|
combine_control_expr = false
|
||||||
|
condense_wildcard_suffixes = true
|
||||||
|
format_code_in_doc_comments = true
|
||||||
|
format_macro_matchers = true
|
||||||
|
format_strings = true
|
||||||
|
|
|
@ -3,9 +3,10 @@ use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::value::AttrValue;
|
use crate::{
|
||||||
use crate::value::VarName;
|
value::{AttrValue, VarName},
|
||||||
use crate::with_text_pos_context;
|
with_text_pos_context,
|
||||||
|
};
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::util;
|
use crate::{
|
||||||
use crate::value::PrimitiveValue;
|
util,
|
||||||
use crate::value::VarName;
|
value::{PrimitiveValue, VarName},
|
||||||
|
};
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
use derive_more;
|
use derive_more;
|
||||||
use element::*;
|
use element::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fmt};
|
||||||
use std::fmt;
|
|
||||||
use util::Coords;
|
use util::Coords;
|
||||||
use xml_ext::*;
|
use xml_ext::*;
|
||||||
|
|
||||||
|
@ -134,12 +134,15 @@ impl EwwConfig {
|
||||||
pub fn get_widgets(&self) -> &HashMap<String, WidgetDefinition> {
|
pub fn get_widgets(&self) -> &HashMap<String, WidgetDefinition> {
|
||||||
&self.widgets
|
&self.widgets
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_windows(&self) -> &HashMap<WindowName, EwwWindowDefinition> {
|
pub fn get_windows(&self) -> &HashMap<WindowName, EwwWindowDefinition> {
|
||||||
&self.windows
|
&self.windows
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_vars(&self) -> &HashMap<VarName, PrimitiveValue> {
|
pub fn get_default_vars(&self) -> &HashMap<VarName, PrimitiveValue> {
|
||||||
&self.initial_variables
|
&self.initial_variables
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_script_vars(&self) -> &Vec<ScriptVar> {
|
pub fn get_script_vars(&self) -> &Vec<ScriptVar> {
|
||||||
&self.script_vars
|
&self.script_vars
|
||||||
}
|
}
|
||||||
|
@ -211,6 +214,7 @@ impl Default for WindowStacking {
|
||||||
|
|
||||||
impl std::str::FromStr for WindowStacking {
|
impl std::str::FromStr for WindowStacking {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let s = s.to_lowercase();
|
let s = s.to_lowercase();
|
||||||
match s.as_str() {
|
match s.as_str() {
|
||||||
|
|
|
@ -141,6 +141,7 @@ impl<'a, 'b> XmlElement<'a, 'b> {
|
||||||
|
|
||||||
format!("<{} {}>", self.tag_name(), attrs)
|
format!("<{} {}>", self.tag_name(), attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tag_name(&self) -> &str {
|
pub fn tag_name(&self) -> &str {
|
||||||
self.0.tag_name().name()
|
self.0.tag_name().name()
|
||||||
}
|
}
|
||||||
|
@ -159,6 +160,7 @@ impl<'a, 'b> XmlElement<'a, 'b> {
|
||||||
.filter(|child| child.is_element() || (child.is_text() && !child.text().unwrap_or_default().is_blank()))
|
.filter(|child| child.is_element() || (child.is_text() && !child.text().unwrap_or_default().is_blank()))
|
||||||
.map(XmlNode::from)
|
.map(XmlNode::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn child_elements(&self) -> impl Iterator<Item = XmlElement> {
|
pub fn child_elements(&self) -> impl Iterator<Item = XmlElement> {
|
||||||
self.0.children().filter(|child| child.is_element()).map(XmlElement)
|
self.0.children().filter(|child| child.is_element()).map(XmlElement)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use crate::config::WindowName;
|
use crate::{config::WindowName, value::VarName};
|
||||||
use crate::value::VarName;
|
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, process::Command, sync::Arc};
|
||||||
use std::process::Command;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::value::{AttrValue, PrimitiveValue};
|
use crate::value::{AttrValue, PrimitiveValue};
|
||||||
|
|
||||||
/// Handler that get's executed to apply the necessary parts of the eww state to a gtk widget.
|
/// Handler that get's executed to apply the necessary parts of the eww state to
|
||||||
/// These are created and initialized in EwwState::resolve.
|
/// a gtk widget. These are created and initialized in EwwState::resolve.
|
||||||
pub struct StateChangeHandler {
|
pub struct StateChangeHandler {
|
||||||
func: Box<dyn Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static>,
|
func: Box<dyn Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static>,
|
||||||
constant_values: HashMap<String, PrimitiveValue>,
|
constant_values: HashMap<String, PrimitiveValue>,
|
||||||
|
@ -57,7 +54,8 @@ impl EwwWindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores the actual state of eww, including the variable state and the window-specific state-change handlers.
|
/// Stores the actual state of eww, including the variable state and the
|
||||||
|
/// window-specific state-change handlers.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EwwState {
|
pub struct EwwState {
|
||||||
windows: HashMap<WindowName, EwwWindowState>,
|
windows: HashMap<WindowName, EwwWindowState>,
|
||||||
|
@ -88,7 +86,8 @@ impl EwwState {
|
||||||
self.windows.clear();
|
self.windows.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the value of a variable, running all registered [StateChangeHandler]s.
|
/// Update the value of a variable, running all registered
|
||||||
|
/// [StateChangeHandler]s.
|
||||||
pub fn update_variable(&mut self, key: VarName, value: PrimitiveValue) -> Result<()> {
|
pub fn update_variable(&mut self, key: VarName, value: PrimitiveValue) -> Result<()> {
|
||||||
if !self.variables_state.contains_key(&key) {
|
if !self.variables_state.contains_key(&key) {
|
||||||
bail!("Tried to set unknown variable '{}'", key);
|
bail!("Tried to set unknown variable '{}'", key);
|
||||||
|
@ -111,7 +110,8 @@ impl EwwState {
|
||||||
|
|
||||||
/// resolves a value if possible, using the current eww_state
|
/// resolves a value if possible, using the current eww_state
|
||||||
/// Expects there to be at max one level of nesting var_refs from local-env.
|
/// Expects there to be at max one level of nesting var_refs from local-env.
|
||||||
/// This means that no elements in the local_env may be var-refs into the local_env again, but only into the global state.
|
/// This means that no elements in the local_env may be var-refs into the
|
||||||
|
/// local_env again, but only into the global state.
|
||||||
pub fn resolve_once<'a>(
|
pub fn resolve_once<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
local_env: &'a HashMap<VarName, AttrValue>,
|
local_env: &'a HashMap<VarName, AttrValue>,
|
||||||
|
@ -134,9 +134,11 @@ impl EwwState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve takes a function that applies a set of fully resolved attribute values to it's gtk widget.
|
/// Resolve takes a function that applies a set of fully resolved attribute
|
||||||
/// Expects there to be at max one level of nesting var_refs from local-env.
|
/// values to it's gtk widget. Expects there to be at max one level of
|
||||||
/// This means that no elements in the local_env may be var-refs into the local_env again, but only into the global state.
|
/// nesting var_refs from local-env. This means that no elements in the
|
||||||
|
/// local_env may be var-refs into the local_env again, but only into the
|
||||||
|
/// global state.
|
||||||
pub fn resolve<F: Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static + Clone>(
|
pub fn resolve<F: Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
window_name: &WindowName,
|
window_name: &WindowName,
|
||||||
|
@ -144,9 +146,11 @@ impl EwwState {
|
||||||
mut needed_attributes: HashMap<String, AttrValue>,
|
mut needed_attributes: HashMap<String, AttrValue>,
|
||||||
set_value: F,
|
set_value: F,
|
||||||
) {
|
) {
|
||||||
// Resolve first collects all variable references and creates a set of unresolved attribute -> VarName pairs.
|
// Resolve first collects all variable references and creates a set of
|
||||||
// additionally, all constant values are looked up and collected, including the values from the local environment
|
// unresolved attribute -> VarName pairs. additionally, all constant values are
|
||||||
// These are then used to generate a StateChangeHandler, which is then executed and registered in the windows state.
|
// looked up and collected, including the values from the local environment
|
||||||
|
// These are then used to generate a StateChangeHandler, which is then executed
|
||||||
|
// and registered in the windows state.
|
||||||
|
|
||||||
let result: Result<_> = try {
|
let result: Result<_> = try {
|
||||||
let window_state = self
|
let window_state = self
|
||||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -16,11 +16,12 @@ use ipc_channel::ipc;
|
||||||
use log;
|
use log;
|
||||||
use pretty_env_logger;
|
use pretty_env_logger;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
use std::path::{Path, PathBuf};
|
collections::HashMap,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use value::PrimitiveValue;
|
use value::{PrimitiveValue, VarName};
|
||||||
use value::VarName;
|
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
@ -221,7 +222,8 @@ fn run_filewatch_thread<P: AsRef<Path>>(
|
||||||
Ok(hotwatch)
|
Ok(hotwatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// detach the process from the terminal, also closing stdout and redirecting stderr into /dev/null
|
/// detach the process from the terminal, also closing stdout and redirecting
|
||||||
|
/// stderr into /dev/null
|
||||||
fn do_detach() {
|
fn do_detach() {
|
||||||
// detach from terminal
|
// detach from terminal
|
||||||
let pid = unsafe { libc::fork() };
|
let pid = unsafe { libc::fork() };
|
||||||
|
@ -238,7 +240,7 @@ fn do_detach() {
|
||||||
libc::close(1);
|
libc::close(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//close stderr to not spam output
|
// close stderr to not spam output
|
||||||
if unsafe { libc::isatty(2) } != 0 {
|
if unsafe { libc::isatty(2) } != 0 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let fd = libc::open(std::ffi::CString::new("/dev/null").unwrap().as_ptr(), libc::O_RDWR);
|
let fd = libc::open(std::ffi::CString::new("/dev/null").unwrap().as_ptr(), libc::O_RDWR);
|
||||||
|
|
|
@ -19,7 +19,8 @@ impl ScriptVarHandler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// clears and stops the currently running poll handles, then opens the new ones as configured
|
/// clears and stops the currently running poll handles, then opens the new
|
||||||
|
/// ones as configured
|
||||||
pub fn setup_command_poll_tasks(&mut self, config: &config::EwwConfig) -> Result<()> {
|
pub fn setup_command_poll_tasks(&mut self, config: &config::EwwConfig) -> Result<()> {
|
||||||
log::info!("reloading handler for poll script vars");
|
log::info!("reloading handler for poll script vars");
|
||||||
self.poll_handles.iter().for_each(|handle| handle.stop());
|
self.poll_handles.iter().for_each(|handle| handle.stop());
|
||||||
|
|
|
@ -3,8 +3,7 @@ use extend::ext;
|
||||||
use grass;
|
use grass;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::{fmt, path::Path};
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub fn parse_scss_from_file<P: AsRef<Path>>(path: P) -> Result<String> {
|
pub fn parse_scss_from_file<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||||
let scss_content = std::fs::read_to_string(path)?;
|
let scss_content = std::fs::read_to_string(path)?;
|
||||||
|
@ -14,7 +13,8 @@ pub fn parse_scss_from_file<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||||
|
|
||||||
#[ext(pub, name = StringExt)]
|
#[ext(pub, name = StringExt)]
|
||||||
impl<T: AsRef<str>> T {
|
impl<T: AsRef<str>> T {
|
||||||
/// check if the string is empty after removing all linebreaks and trimming whitespace
|
/// check if the string is empty after removing all linebreaks and trimming
|
||||||
|
/// whitespace
|
||||||
fn is_blank(self) -> bool {
|
fn is_blank(self) -> bool {
|
||||||
self.as_ref().replace('\n', "").trim().is_empty()
|
self.as_ref().replace('\n', "").trim().is_empty()
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ impl fmt::Display for Coords {
|
||||||
|
|
||||||
impl std::str::FromStr for Coords {
|
impl std::str::FromStr for Coords {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
let (x, y) = s.split_once('x').ok_or_else(|| anyhow!("must be formatted like 200x500"))?;
|
let (x, y) = s.split_once('x').ok_or_else(|| anyhow!("must be formatted like 200x500"))?;
|
||||||
Ok(Coords(x.parse()?, y.parse()?))
|
Ok(Coords(x.parse()?, y.parse()?))
|
||||||
|
|
17
src/value.rs
17
src/value.rs
|
@ -4,8 +4,7 @@ use lazy_static::lazy_static;
|
||||||
use ref_cast::RefCast;
|
use ref_cast::RefCast;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::{convert::TryFrom, fmt};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Deserialize, Serialize, derive_more::From)]
|
#[derive(Clone, PartialEq, Deserialize, Serialize, derive_more::From)]
|
||||||
pub enum PrimitiveValue {
|
pub enum PrimitiveValue {
|
||||||
|
@ -32,7 +31,8 @@ impl fmt::Debug for PrimitiveValue {
|
||||||
impl std::str::FromStr for PrimitiveValue {
|
impl std::str::FromStr for PrimitiveValue {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
/// parses the value, trying to turn it into a number and a boolean first, before deciding that it is a string.
|
/// parses the value, trying to turn it into a number and a boolean first,
|
||||||
|
/// before deciding that it is a string.
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
Ok(PrimitiveValue::parse_string(s))
|
Ok(PrimitiveValue::parse_string(s))
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ fn remove_surrounding(s: &str, surround: char) -> &str {
|
||||||
|
|
||||||
impl TryFrom<PrimitiveValue> for String {
|
impl TryFrom<PrimitiveValue> for String {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(x: PrimitiveValue) -> Result<Self> {
|
fn try_from(x: PrimitiveValue) -> Result<Self> {
|
||||||
x.as_string()
|
x.as_string()
|
||||||
}
|
}
|
||||||
|
@ -51,6 +52,7 @@ impl TryFrom<PrimitiveValue> for String {
|
||||||
|
|
||||||
impl TryFrom<PrimitiveValue> for f64 {
|
impl TryFrom<PrimitiveValue> for f64 {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(x: PrimitiveValue) -> Result<Self> {
|
fn try_from(x: PrimitiveValue) -> Result<Self> {
|
||||||
x.as_f64()
|
x.as_f64()
|
||||||
}
|
}
|
||||||
|
@ -58,6 +60,7 @@ impl TryFrom<PrimitiveValue> for f64 {
|
||||||
|
|
||||||
impl TryFrom<PrimitiveValue> for bool {
|
impl TryFrom<PrimitiveValue> for bool {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(x: PrimitiveValue) -> Result<Self> {
|
fn try_from(x: PrimitiveValue) -> Result<Self> {
|
||||||
x.as_bool()
|
x.as_bool()
|
||||||
}
|
}
|
||||||
|
@ -70,13 +73,15 @@ impl From<&str> for PrimitiveValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrimitiveValue {
|
impl PrimitiveValue {
|
||||||
/// parses the value, trying to turn it into a number and a boolean first, before deciding that it is a string.
|
/// parses the value, trying to turn it into a number and a boolean first,
|
||||||
|
/// before deciding that it is a string.
|
||||||
pub fn parse_string(s: &str) -> Self {
|
pub fn parse_string(s: &str) -> Self {
|
||||||
s.parse()
|
s.parse()
|
||||||
.map(PrimitiveValue::Number)
|
.map(PrimitiveValue::Number)
|
||||||
.or_else(|_| s.parse().map(PrimitiveValue::Boolean))
|
.or_else(|_| s.parse().map(PrimitiveValue::Boolean))
|
||||||
.unwrap_or_else(|_| PrimitiveValue::String(remove_surrounding(s, '\'').to_string()))
|
.unwrap_or_else(|_| PrimitiveValue::String(remove_surrounding(s, '\'').to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_string(&self) -> Result<String> {
|
pub fn as_string(&self) -> Result<String> {
|
||||||
match self {
|
match self {
|
||||||
PrimitiveValue::String(x) => Ok(x.clone()),
|
PrimitiveValue::String(x) => Ok(x.clone()),
|
||||||
|
@ -84,6 +89,7 @@ impl PrimitiveValue {
|
||||||
PrimitiveValue::Boolean(x) => Ok(format!("{}", x)),
|
PrimitiveValue::Boolean(x) => Ok(format!("{}", x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_f64(&self) -> Result<f64> {
|
pub fn as_f64(&self) -> Result<f64> {
|
||||||
match self {
|
match self {
|
||||||
PrimitiveValue::Number(x) => Ok(*x),
|
PrimitiveValue::Number(x) => Ok(*x),
|
||||||
|
@ -93,6 +99,7 @@ impl PrimitiveValue {
|
||||||
_ => Err(anyhow!("{:?} is not an f64", &self)),
|
_ => Err(anyhow!("{:?} is not an f64", &self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bool(&self) -> Result<bool> {
|
pub fn as_bool(&self) -> Result<bool> {
|
||||||
match self {
|
match self {
|
||||||
PrimitiveValue::Boolean(x) => Ok(*x),
|
PrimitiveValue::Boolean(x) => Ok(*x),
|
||||||
|
@ -145,12 +152,14 @@ impl AttrValue {
|
||||||
_ => Err(anyhow!("{:?} is not a string", self)),
|
_ => Err(anyhow!("{:?} is not a string", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_f64(&self) -> Result<f64> {
|
pub fn as_f64(&self) -> Result<f64> {
|
||||||
match self {
|
match self {
|
||||||
AttrValue::Concrete(x) => Ok(x.as_f64()?),
|
AttrValue::Concrete(x) => Ok(x.as_f64()?),
|
||||||
_ => Err(anyhow!("{:?} is not an f64", self)),
|
_ => Err(anyhow!("{:?} is not an f64", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bool(&self) -> Result<bool> {
|
pub fn as_bool(&self) -> Result<bool> {
|
||||||
match self {
|
match self {
|
||||||
AttrValue::Concrete(x) => Ok(x.as_bool()?),
|
AttrValue::Concrete(x) => Ok(x.as_bool()?),
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::config::{element, WindowName};
|
use crate::{
|
||||||
use crate::eww_state::*;
|
config::{element, WindowName},
|
||||||
use crate::value::{AttrValue, VarName};
|
eww_state::*,
|
||||||
|
value::{AttrValue, VarName},
|
||||||
|
};
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
||||||
|
@ -11,8 +13,8 @@ pub mod widget_definitions;
|
||||||
|
|
||||||
const CMD_STRING_PLACEHODLER: &str = "{}";
|
const CMD_STRING_PLACEHODLER: &str = "{}";
|
||||||
|
|
||||||
/// Run a command that was provided as an attribute. This command may use a placeholder ('{}')
|
/// Run a command that was provided as an attribute. This command may use a
|
||||||
/// which will be replaced by the value provided as [arg]
|
/// placeholder ('{}') which will be replaced by the value provided as [arg]
|
||||||
pub fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
|
pub fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
|
||||||
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
|
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
|
||||||
if let Err(e) = Command::new("bash").arg("-c").arg(cmd).output() {
|
if let Err(e) = Command::new("bash").arg("-c").arg(cmd).output() {
|
||||||
|
@ -30,12 +32,14 @@ struct BuilderArgs<'a, 'b, 'c, 'd, 'e> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a [gtk::Widget] from a [element::WidgetUse].
|
/// Generate a [gtk::Widget] from a [element::WidgetUse].
|
||||||
/// The widget_use may be using a builtin widget, or a custom [element::WidgetDefinition].
|
/// The widget_use may be using a builtin widget, or a custom
|
||||||
|
/// [element::WidgetDefinition].
|
||||||
///
|
///
|
||||||
/// Also registers all the necessary state-change handlers in the eww_state.
|
/// Also registers all the necessary state-change handlers in the eww_state.
|
||||||
///
|
///
|
||||||
/// This may return `Err` in case there was an actual error while parsing or resolving the widget,
|
/// This may return `Err` in case there was an actual error while parsing or
|
||||||
/// Or `Ok(None)` if the widget_use just didn't match any widget name.
|
/// resolving the widget, Or `Ok(None)` if the widget_use just didn't match any
|
||||||
|
/// widget name.
|
||||||
pub fn widget_use_to_gtk_widget(
|
pub fn widget_use_to_gtk_widget(
|
||||||
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
|
@ -48,18 +52,20 @@ pub fn widget_use_to_gtk_widget(
|
||||||
let gtk_widget = if let Some(builtin_gtk_widget) = builtin_gtk_widget {
|
let gtk_widget = if let Some(builtin_gtk_widget) = builtin_gtk_widget {
|
||||||
builtin_gtk_widget
|
builtin_gtk_widget
|
||||||
} else if let Some(def) = widget_definitions.get(widget.name.as_str()) {
|
} else if let Some(def) = widget_definitions.get(widget.name.as_str()) {
|
||||||
//let mut local_env = local_env.clone();
|
// let mut local_env = local_env.clone();
|
||||||
|
|
||||||
// the attributes that are set on the widget need to be resolved as far as possible.
|
// the attributes that are set on the widget need to be resolved as far as
|
||||||
// If an attribute is a variable reference, it must either reference a variable in the current local_env, or in the global state.
|
// possible. If an attribute is a variable reference, it must either reference a
|
||||||
// As we are building widgets from the outer most to the most nested, we can resolve attributes at every step.
|
// variable in the current local_env, or in the global state. As we are building
|
||||||
// This way, any definition that is affected by changes in the eww_state will be directly linked to the eww_state's value.
|
// widgets from the outer most to the most nested, we can resolve attributes at
|
||||||
// Example:
|
// every step. This way, any definition that is affected by changes in the
|
||||||
// foo="{{in_eww_state}}" => attr_in_child="{{foo}}" => attr_in_nested_child="{{attr_in_child}}"
|
// eww_state will be directly linked to the eww_state's value. Example:
|
||||||
// will be resolved step by step. This code will first resolve attr_in_child to directly be attr_in_child={{in_eww_state}}.
|
// foo="{{in_eww_state}}" => attr_in_child="{{foo}}" =>
|
||||||
// then, in the widget_use_to_gtk_widget call of that child element,
|
// attr_in_nested_child="{{attr_in_child}}" will be resolved step by step. This
|
||||||
// attr_in_nested_child will again be resolved to point to the value of attr_in_child,
|
// code will first resolve attr_in_child to directly be
|
||||||
// and thus: attr_in_nested_child="{{in_eww_state}}"
|
// attr_in_child={{in_eww_state}}. then, in the widget_use_to_gtk_widget call of
|
||||||
|
// that child element, attr_in_nested_child will again be resolved to point to
|
||||||
|
// the value of attr_in_child, and thus: attr_in_nested_child="{{in_eww_state}}"
|
||||||
let resolved_widget_attr_env = widget
|
let resolved_widget_attr_env = widget
|
||||||
.attrs
|
.attrs
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -93,13 +99,14 @@ pub fn widget_use_to_gtk_widget(
|
||||||
Ok(gtk_widget)
|
Ok(gtk_widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// build a [gtk::Widget] out of a [element::WidgetUse] that uses a **builtin widget**.
|
/// build a [gtk::Widget] out of a [element::WidgetUse] that uses a **builtin
|
||||||
/// User defined widgets are handled by [widget_use_to_gtk_widget].
|
/// widget**. User defined widgets are handled by [widget_use_to_gtk_widget].
|
||||||
///
|
///
|
||||||
/// Also registers all the necessary handlers in the `eww_state`.
|
/// Also registers all the necessary handlers in the `eww_state`.
|
||||||
///
|
///
|
||||||
/// This may return `Err` in case there was an actual error while parsing or resolving the widget,
|
/// This may return `Err` in case there was an actual error while parsing or
|
||||||
/// Or `Ok(None)` if the widget_use just didn't match any widget name.
|
/// resolving the widget, Or `Ok(None)` if the widget_use just didn't match any
|
||||||
|
/// widget name.
|
||||||
fn build_builtin_gtk_widget(
|
fn build_builtin_gtk_widget(
|
||||||
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
use super::{run_command, BuilderArgs};
|
use super::{run_command, BuilderArgs};
|
||||||
use crate::config;
|
use crate::{
|
||||||
use crate::eww_state;
|
config, eww_state, resolve_block,
|
||||||
use crate::resolve_block;
|
value::{AttrValue, PrimitiveValue},
|
||||||
use crate::value::{AttrValue, PrimitiveValue};
|
};
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
use gtk::prelude::*;
|
use gtk::{prelude::*, ImageExt};
|
||||||
use gtk::ImageExt;
|
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use std::cell::RefCell;
|
use std::{cell::RefCell, path::Path, rc::Rc};
|
||||||
use std::path::Path;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use gdk_pixbuf;
|
use gdk_pixbuf;
|
||||||
|
|
||||||
|
@ -259,7 +256,8 @@ fn parse_align(o: &str) -> Result<gtk::Align> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_first_map<W: IsA<gtk::Widget>, F: Fn(&W) + 'static>(widget: &W, func: F) {
|
fn connect_first_map<W: IsA<gtk::Widget>, F: Fn(&W) + 'static>(widget: &W, func: F) {
|
||||||
// TODO it would be better to actually remove the connect_map after first map, but that would be highly annoying to implement...
|
// TODO it would be better to actually remove the connect_map after first map,
|
||||||
|
// but that would be highly annoying to implement...
|
||||||
let is_first_map = std::rc::Rc::new(std::cell::RefCell::new(true));
|
let is_first_map = std::rc::Rc::new(std::cell::RefCell::new(true));
|
||||||
widget.connect_map(move |w| {
|
widget.connect_map(move |w| {
|
||||||
if is_first_map.replace(false) {
|
if is_first_map.replace(false) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue