745 Commits
v0.1.1 ... main

Author SHA1 Message Date
Byson94
39a67a4564 chore: run cargo fmt 2026-01-22 20:14:34 +05:30
Byson94
4b80630b7e Merge pull request #24 from Square-face/main
Fix: make nix flake up to date
2026-01-18 16:37:21 +05:30
Linus Michelsson
dee7979cd5 fix: updated flake lock and switched to gtk4 2026-01-18 07:04:34 +01:00
Byson94
8709371e4e Fix typo in README.md for Improved Interface 2026-01-07 21:18:31 +05:30
Byson94
e4ab87bc2a feat: touch support to scale widget 2026-01-04 15:26:56 +05:30
Byson94
37b57aee60 feat: implement orientation for scale widget 2026-01-02 13:08:53 +05:30
Byson94
33ef1720e3 feat: return data early if mutations is empty 2026-01-01 12:55:40 +05:30
Byson94
c41a495ee0 localsignal: fix no compiled ast warning returning early 2026-01-01 12:49:39 +05:30
Byson94
47f93e9cab chore: run cargo fmt 2026-01-01 12:46:56 +05:30
Byson94
9e91ae61a5 Merge pull request #19 from BinaryHarbinger/main
chore: Change source of Binarydots example
2026-01-01 11:06:58 +05:30
BinaryHarbinger
87cc157055 chore: Change source of Binarydots example 2025-12-31 20:43:19 +03:00
Byson94
29983ab9da feat: add eval_ignore prop to all widgets 2025-12-30 09:25:00 +05:30
Byson94
ff9db50831 feat: add mutations property to localsignal 2025-12-28 19:41:01 +05:30
Byson94
4cb6ac05d3 docs: remove unnecessary feat in changelog 2025-12-28 14:25:55 +05:30
Byson94
73a64944b8 docs(changelog): fix removed section in unreleased 2025-12-25 10:54:16 +05:30
Byson94
d25c2db420 chore: cargo fmt 2025-12-24 11:19:09 +05:30
Byson94
216775f55a feat(img wdgt): custom rendering + new feats 2025-12-24 11:18:33 +05:30
Byson94
ad79e81c50 feat: new image widget features & fully remove icon 2025-12-23 14:57:52 +05:30
Byson94
df7226d06c feat: remove icon widget 2025-12-23 14:50:16 +05:30
Byson94
6e03473133 feat: replace image widget rendering 2025-12-22 19:17:27 +05:30
Byson94
36c58e211d feat: update props of icon widget 2025-12-22 18:18:26 +05:30
Byson94
70de347bcf fix: clockwise prop not working on circ progress 2025-12-10 14:52:56 +05:30
Byson94
2cbf64e250 feat: add text and show_text prop to progressbar widget 2025-12-09 18:34:44 +05:30
Byson94
a14e559c80 feat: rename WidgetNode::Slider to WidgetNode::Scale 2025-12-09 17:04:32 +05:30
Byson94
59fb1b85eb feat: remove ewwii_anims from cargo.toml 2025-12-08 21:32:06 +05:30
Byson94
4197a863e5 feat: change doccomment description of rhai_impl crate 2025-12-08 19:49:09 +05:30
Byson94
4293c6877d feat: migrate most legacycontroller to gestureclick
This provides touch support to widgets
2025-12-07 14:14:35 +05:30
Byson94
f393627932 feat: parse widget_action ucontainer actions like shell 2025-12-06 16:14:53 +05:30
Byson94
c54ce27505 feat: add features section in readme 2025-12-02 19:01:32 +05:30
Byson94
97518eb49c chore: run cargo fmt 2025-11-29 20:45:31 +05:30
Byson94
ddce15481f feat: remove remove action from widget_action utility 2025-11-29 19:49:52 +05:30
Byson94
43721426e8 feat: proptotype widgetaction utility widget 2025-11-28 20:25:18 +05:30
Byson94
6baa9c7858 feat: add transition_duration prop to stack widget 2025-11-26 20:18:28 +05:30
Byson94
8ec080a290 feat: add add/remove-class subcommand to wc command 2025-11-26 19:15:28 +05:30
Byson94
166c440978 chore: cargo fmt 2025-11-25 21:05:24 +05:30
Byson94
6e9dca9d42 feat: add placeholder property to input widget 2025-11-25 21:04:32 +05:30
Byson94
b50f41b1e0 feat: add propety-update argument to widget control command 2025-11-25 20:49:26 +05:30
Byson94
357dcaacbc fix: doc comment of epapi register_function 2025-11-24 19:33:49 +05:30
Byson94
61e681e6bd chore: run cargo fmt 2025-11-24 19:26:30 +05:30
Byson94
ebd4264621 fix: localbind not finding properties of range subclasses 2025-11-23 17:49:22 +05:30
Byson94
4167c64fde feat: fix register_function docs in epapi 2025-11-22 17:41:24 +05:30
Byson94
6456b2998d feat: add link to documentation in readme 2025-11-22 17:39:16 +05:30
Byson94
4de5ab3c59 chore: commit change in cargo.lock 2025-11-22 12:08:07 +05:30
Byson94
04ca79a5af feat: improve epapi; bump epapi to 0.7.0; 2025-11-22 12:05:30 +05:30
Byson94
0d803fd962 chore: run cargo fmt 2025-11-22 11:13:24 +05:30
Byson94
8406c86117 feat: bump ewwii_plugin_api version 2025-11-21 21:53:41 +05:30
Byson94
1aee3163e0 feat: make register_function on ewwii_plugin_api register directly 2025-11-21 21:53:19 +05:30
Byson94
9aec974e9e feat: add warning on ewwii_plugin_api until it is fixed 2025-11-21 18:06:52 +05:30
Byson94
4de148be58 feat: remove debug in action_with_engine 2025-11-20 21:02:23 +05:30
Byson94
3f48178333 feat: matching rhai features across all crates 2025-11-19 14:35:55 +05:30
Byson94
8af01e44f2 feat: improve plugin loading order 2025-11-17 20:05:26 +05:30
Byson94
a76440f242 feat: add trace log in action_with_engine 2025-11-16 20:29:14 +05:30
Byson94
49c0d14ace feat: update wc actions to take vec of strings 2025-11-13 19:27:46 +05:30
Byson94
6a6856192c feat: make wc add eval rhai code instead of .ui 2025-11-08 18:47:09 +05:30
Byson94
b821d8bb6a feat: add widget-control command 2025-11-08 17:09:51 +05:30
Byson94
e12d5f7fb9 feat: add gtk_ui function for loading .ui 2025-11-07 20:22:56 +05:30
Byson94
26ee4e5560 chore: cargo fmt 2025-11-01 19:38:27 +05:30
Byson94
622a9c1b06 feat: bump to ewwii 0.3.1 2025-11-01 19:37:36 +05:30
Byson94
6b0e94698b fix: localsignal not working for non-gchararray props 2025-11-01 18:43:41 +05:30
Byson94
08e1f2d5f3 fix: circularprogress not updating to dyn vars 2025-11-01 18:30:43 +05:30
Byson94
b53431d861 chore: run cargo fmt 2025-11-01 12:22:30 +05:30
Byson94
327b223998 chore: add 0.3.0 release in changelog 2025-11-01 12:21:27 +05:30
Byson94
e26cd16ef9 fix: ensure full teardown of signals in reload 2025-11-01 10:29:04 +05:30
Byson94
669e7e9566 fix: minor issues with localsignals 2025-10-31 21:51:57 +05:30
Byson94
9bb8a9e3dd feat: turn localbind from a widget to a utility 2025-10-30 21:40:14 +05:30
Byson94
249cacdd0a chore: run cargo fmt 2025-10-30 19:47:28 +05:30
Byson94
167fe26f30 chore: notedown changes in changelog 2025-10-30 19:47:15 +05:30
Byson94
44a86e0937 feat: make localsignal simpler through localbind container 2025-10-30 19:45:43 +05:30
Byson94
f5e1b61dcf Revert "wip: adding localsignal support for all widget"
This reverts commit 7dc64014fd.
2025-10-30 15:11:08 +05:30
Byson94
1ca23373e1 Revert "wip: add localsignal support to eventbox"
This reverts commit 7095cb6637.
2025-10-30 15:11:06 +05:30
Byson94
78dafcc5b3 Revert "wip: add localsignal handling for basic props"
This reverts commit 74718b064f.
2025-10-30 15:11:05 +05:30
Byson94
e92ccab3d1 Revert "wip: adding more support to localsignal"
This reverts commit 19b790d4fa.
2025-10-30 15:11:03 +05:30
Byson94
8820a0f274 Revert "wip: making localsignal only bind once"
This reverts commit 8f15274fb9.
2025-10-30 15:11:02 +05:30
Byson94
9c15f8aed1 Revert "feat: make localsignal id relative to its props"
This reverts commit 9458d95222.
2025-10-30 15:11:00 +05:30
Byson94
f5ad63ed33 Revert "feat: moving initial of localsignal to a different place"
This reverts commit b3043afe04.
2025-10-30 15:10:59 +05:30
Byson94
7abc39d051 Revert "feat: add clear fn for localsignal store"
This reverts commit 60af15fb8d.
2025-10-30 15:10:58 +05:30
Byson94
47745fa501 Revert "feat: mvoe from widget binding to notify_local"
This reverts commit 2ac527bcbc.
2025-10-30 15:10:56 +05:30
Byson94
4c2e460127 Revert "feat: add localsignal support to many more widgets"
This reverts commit 902ca67c58.
2025-10-30 15:10:55 +05:30
Byson94
fa567b06d0 Revert "feat: add localsignal support to even moer widgets"
This reverts commit bfe1d248a4.
2025-10-30 15:10:53 +05:30
Byson94
892c09512c Revert "feat: add default text/markup values for gtk_label"
This reverts commit b369f648db.
2025-10-30 15:10:47 +05:30
Byson94
8be2f01058 Revert "fix: gtk_label default text/markup value being &str"
This reverts commit 91e7a86e7a.
2025-10-30 15:10:44 +05:30
Byson94
673ef2e09d Revert "feat: set gtk box space evenly to true"
This reverts commit 425ef5020b.
2025-10-30 15:10:36 +05:30
Byson94
cd271ed283 Revert "feat: moving all localsignal into a localbind wrapper"
This reverts commit 98e370ac60.
2025-10-30 15:10:19 +05:30
Byson94
98e370ac60 feat: moving all localsignal into a localbind wrapper 2025-10-30 15:00:00 +05:30
Byson94
425ef5020b feat: set gtk box space evenly to true 2025-10-30 14:12:10 +05:30
Byson94
91e7a86e7a fix: gtk_label default text/markup value being &str 2025-10-30 14:09:14 +05:30
Byson94
b369f648db feat: add default text/markup values for gtk_label 2025-10-30 14:07:18 +05:30
Byson94
bfe1d248a4 feat: add localsignal support to even moer widgets 2025-10-30 13:39:55 +05:30
Byson94
902ca67c58 feat: add localsignal support to many more widgets 2025-10-29 20:07:26 +05:30
Byson94
2ac527bcbc feat: mvoe from widget binding to notify_local 2025-10-29 16:16:58 +05:30
Byson94
60af15fb8d feat: add clear fn for localsignal store 2025-10-29 14:51:57 +05:30
Byson94
b3043afe04 feat: moving initial of localsignal to a different place 2025-10-29 14:23:26 +05:30
Byson94
9458d95222 feat: make localsignal id relative to its props 2025-10-29 13:56:25 +05:30
Byson94
8f15274fb9 wip: making localsignal only bind once 2025-10-29 13:33:41 +05:30
Byson94
19b790d4fa wip: adding more support to localsignal 2025-10-28 22:17:12 +05:30
Byson94
74718b064f wip: add localsignal handling for basic props 2025-10-28 21:51:46 +05:30
Byson94
7095cb6637 wip: add localsignal support to eventbox 2025-10-28 20:52:16 +05:30
Byson94
7dc64014fd wip: adding localsignal support for all widget 2025-10-28 20:14:32 +05:30
Byson94
66bc9f6d74 wip: localsignal for fast widget update 2025-10-27 22:25:49 +05:30
Byson94
4b3240b9b9 fix: example in ewii_plugin_api 2025-10-27 18:52:15 +05:30
Byson94
3803141f31 chore: run cargo fmt (again) 2025-10-27 18:13:21 +05:30
Byson94
2ed91786a3 chore: fix merge conflicts 2025-10-27 18:10:51 +05:30
Byson94
5e827a73c8 chore: run cargo fmt 2025-10-27 18:09:26 +05:30
Byson94
58957ec597 fix: poll/listen variables not working in other files 2025-10-27 18:08:56 +05:30
Byson94
abafe1b6fd fix: poll/listen variables not working in other files 2025-10-27 18:05:55 +05:30
Byson94
6536035e19 feat: add list_fns func to slib 2025-10-26 20:49:52 +05:30
Byson94
187a367977 feat: add support for circular-progress widget 2025-10-26 14:05:54 +05:30
Byson94
e0ae5c97d4 feat: add lifetime flag to update command 2025-10-25 15:47:08 +05:30
Byson94
59b3cdd0dc feat: implement recv drain in plugin load func 2025-10-24 21:00:03 +05:30
Byson94
5cf8659f92 feat: add default_select property to flowbox 2025-10-24 19:57:40 +05:30
Byson94
0b14629dd7 feat: move to rhai_trace 0.3.1 to not panic on certain err 2025-10-24 19:02:28 +05:30
Byson94
726093f534 fix: requiring child-index property on children of flowbox 2025-10-24 18:56:23 +05:30
Byson94
39a389b90c feat: add widget_name property to all widgets 2025-10-23 20:14:12 +05:30
Byson94
140643d4ec feat: add focusable property to all widget 2025-10-23 19:43:14 +05:30
Byson94
f52164da87 chore: run cargo fmt 2025-10-23 18:44:22 +05:30
Byson94
926c5bae7e feat: add flowbox widget 2025-10-22 22:11:51 +05:30
Byson94
ac8375bf47 fix: dont print error if message is '' 2025-10-22 18:03:21 +05:30
Byson94
58667c77ed fix: signals not exposed to other mod on first launch 2025-10-21 19:45:27 +05:30
Byson94
b06c3e0e3f feat: add new properties to eventbox 2025-10-21 18:52:30 +05:30
Byson94
21d50ed7e6 chore: fix one small comment 2025-10-20 22:23:24 +05:30
Byson94
5f576315fe chore: document change in changelog 2025-10-20 12:11:40 +05:30
Byson94
da57afb395 feat: change fnonce of register_function to fn 2025-10-20 12:06:01 +05:30
Byson94
c0ef865920 feat: add slib module for handling shared libs 2025-10-20 11:43:14 +05:30
Byson94
3afc8fa9ca fix: few eventbox drop target issue 2025-10-18 14:43:42 +05:30
Byson94
7a4191df95 chore(doc): improve ewwii_plugin_api doc comments 2025-10-14 21:32:24 +05:30
Byson94
18832c2956 feat(opt): add --with-plugin flag to daemon command 2025-10-14 19:58:46 +05:30
Byson94
4974df29d6 chore: feature 'Binary Dots' in readme 2025-10-14 17:58:20 +05:30
Byson94
b72700fbd2 Merge pull request #9 from BinaryHarbinger/main
Setting style provider priority to 900 still uses global theme as fallback just like GTK3 version.
2025-10-14 17:43:40 +05:30
BinaryHarbinger
361fe49001 fix: Style provider priority 'APPLICATION >> 900' 2025-10-13 20:57:24 +03:00
Byson94
db5e2055e1 chore: run cargofmt 2025-10-13 20:25:59 +05:30
Byson94
3ef6861b62 fix: remove VALBEF println clutter 2025-10-13 20:25:27 +05:30
Byson94
3156b2b85a Merge pull request #8 from Cyclic007/patch-1
add activateLinux example
2025-10-13 19:30:53 +05:30
Byson94
d2f8d3dfe3 Merge branch 'main' into iidev 2025-10-13 19:26:42 +05:30
Byson94
fc6f154021 Merge pull request #6 from BinaryHarbinger/main
Add additional support for dash
2025-10-13 19:24:39 +05:30
Byson94
f3b2f4bce7 fix(shell): move code to avoid repetetion 2025-10-13 19:14:58 +05:30
Byson94
edad7fb3b7 fix: inconsistencies between rhai_impl and ewwii_plugin_api 2025-10-13 19:02:22 +05:30
Cyclic
3631b67496 Create ewwii.scss 2025-10-12 14:47:54 +00:00
Cyclic
b118409e83 setup new example rhai 2025-10-12 14:47:12 +00:00
Byson94
34706b1ae8 fix: fix dep inconsistency through re-export 2025-10-12 18:50:40 +05:30
BinaryHarbinger
8927256f39 Add additional support for dash
Automatically detect dash and use it instead of sh if available
2025-10-12 16:11:05 +03:00
Byson94
6d667538db chore(remove): LICENSE-MIT which came after rebase 2025-10-12 15:18:38 +05:30
Byson94
54145617b5 fix(ci): incomplete 'check no-backend' step 2025-10-12 14:44:09 +05:30
Byson94
49057eec5d fix: all doctest err in ewwii_plugin_api 2025-10-12 14:36:23 +05:30
Byson94
b144571c9a fix: .gitignore conflict on merge 2025-10-12 14:26:49 +05:30
Byson94
30a4b63f00 fix: solve more cargo test issus 2025-10-12 14:11:59 +05:30
Byson94
0e2307ea03 fix(ci): Satisfying doctest 2025-10-12 14:11:59 +05:30
Byson94
dcdcc2dbc5 fix: fixing doctest issues with auto_plugin macro 2025-10-12 14:11:59 +05:30
Byson94
9a37ab6e7b fix(ci): using arch linux docker container 2025-10-12 14:11:59 +05:30
Byson94
b398ea4749 feat: adding panic handling 2025-10-12 14:11:59 +05:30
Byson94
c29cc74de0 feat: added onkeypress and onkeyrelease props 2025-10-12 14:11:59 +05:30
Byson94
f571c406c7 fix: fixed old widget creeping in issue 2025-10-12 14:11:59 +05:30
Byson94
517b31d62a fix(ci): fixing packages 2025-10-12 14:11:59 +05:30
Byson94
a6953310b1 chore: cargo fmt 2025-10-12 14:11:59 +05:30
Byson94
46591b1b8a feat: making examples in readme look good 2025-10-12 14:11:58 +05:30
Byson94
67c789ae41 fix(ci): Fixing indentation 2025-10-12 14:11:58 +05:30
Byson94
5ce4cedd3e fix(ci): Working on fixing the ci not running issue 2025-10-12 14:11:58 +05:30
Byson94
df5c4a0678 chore: added info in changelog 2025-10-12 14:11:58 +05:30
Byson94
29176aacf0 chore: fixing ewwii_plugin_api versioning issue 2025-10-12 14:11:58 +05:30
Byson94
345a57c465 feat: added max docs and 1 feat in ewwii_plugin_api 2025-10-12 14:11:58 +05:30
Byson94
bf1e1622ac fix: fixing feature issue in widget_backend.rs 2025-10-12 14:11:58 +05:30
Byson94
b9ce839f83 style: fixing ewwii_plugin_api Cargo.toml style 2025-10-12 14:11:58 +05:30
Byson94
8d025d1958 feat: improving the ewwii_plugin_api crate and fixing issues 2025-10-12 14:11:58 +05:30
Byson94
732acbdf5c feat: more api changes in ewwii_plugin_api 2025-10-12 14:11:58 +05:30
Byson94
975ebc7ee5 feat: improving docs and comments on ewwii_plugin_api 2025-10-12 14:11:58 +05:30
Byson94
03baaf3099 chore: fixing ewwii_plguin_api version issue 2025-10-12 14:11:58 +05:30
Byson94
7ba9d95f96 feat: added the ability to modify widgets 2025-10-12 14:11:58 +05:30
Byson94
3d08b3e873 chore: added change in changelog 2025-10-12 14:11:58 +05:30
Byson94
fa31aa9299 fix(rhai): fixing poll/listen scope not available on all modules 2025-10-12 14:11:58 +05:30
Byson94
7f72d0805e chore: Updating Cargo.lock 2025-10-12 14:11:58 +05:30
Byson94
ac5432c93c fix: fixing ewwii_plugin_api version 2025-10-12 14:11:58 +05:30
Byson94
19db669204 feat: expanding ewwii_plugin_api 2025-10-12 14:11:58 +05:30
Byson94
e13bb1f551 feat: added tools for exporting plugins 2025-10-12 14:11:58 +05:30
Byson94
8fbad0c9be feat: added option to list all widget ids in plugin sys 2025-10-12 14:11:58 +05:30
Byson94
994ee6de29 feat: added documentation for ewwii_plugin_api 2025-10-12 14:11:58 +05:30
Byson94
0bd437603b fix(ci): fixing dependencies on gtk4 2025-10-12 14:11:58 +05:30
Byson94
1891165d50 feat: updating changelog 2025-10-12 14:11:58 +05:30
Byson94
45657e145c feat: added readme and docs to plugin_api 2025-10-12 14:11:58 +05:30
Byson94
ca63ebe504 feat: fixing few errors and improving other stuff 2025-10-12 14:11:58 +05:30
Byson94
66951f8fba chore: cargo fmt 2025-10-12 14:11:58 +05:30
Byson94
609e023f9b fix: fixing issue templates 2025-10-12 14:11:58 +05:30
Byson94
294ae868a6 feat: Updating changelog 2025-10-12 14:11:58 +05:30
Byson94
0e15ffa23a feat: getting rhai mod working via plugin 2025-10-12 14:11:58 +05:30
Byson94
5f62a00c60 feat: got library system loading working 2025-10-12 14:11:58 +05:30
Byson94
bf1b24bb95 wip: bin level plugin system 2025-10-12 14:11:58 +05:30
Byson94
27dacde798 fix: fixed focusable not working 2025-10-12 14:11:58 +05:30
Byson94
9004f186fa feat: adding the release date in changelog 2025-10-12 14:11:58 +05:30
Byson94
b9e6e8ba0c fix: fixing minor issues 2025-10-12 14:11:58 +05:30
Byson94
ed15ddfaaa feat: removed centerbox widget 2025-10-12 14:11:58 +05:30
Byson94
881910f1b3 GREATEST FEAT: Achived sticky windows in x11 2025-10-12 14:11:58 +05:30
Byson94
7936e4f881 fix: fixed gif images not working 2025-10-12 14:11:58 +05:30
Byson94
9072ff8525 fix: did a simple fix over the scale drag issue 2025-10-12 14:11:58 +05:30
Byson94
1c85310c22 chore: Cargo.lock update 2025-10-12 14:11:58 +05:30
Byson94
b65c1b5c23 feat: added deprecated section in changelog 2025-10-12 14:11:58 +05:30
Byson94
2840138464 feat: added information about alpha release in changelog 2025-10-12 14:11:58 +05:30
Byson94
205fe2f9bf fix: fixing all compiler warnings 2025-10-12 14:11:58 +05:30
Byson94
6a7e77029d feat: fixed image wdgt and added icon wdgt 2025-10-12 14:11:58 +05:30
Byson94
f36cb58237 fix: no classes to window to prevent x11 issues 2025-10-12 14:11:58 +05:30
Byson94
c14f2b4fc6 feat: fixing warnings & messin with x11 2025-10-12 14:11:58 +05:30
Byson94
243408d176 fix(x11): fixing widget not realized before getting surface 2025-10-12 14:11:58 +05:30
Byson94
b647eb36af fix: fixing the ewwii window close order 2025-10-12 14:11:58 +05:30
Byson94
4238b5fe9a feat: fixing application loop issues by doing manual one 2025-10-12 14:11:58 +05:30
Byson94
3120d7556e feat: fixing very single gtk4 migration error! yippee! 2025-10-12 14:11:58 +05:30
Byson94
a9ccb25cae fix: fixing one of the last few err (i think so) 2025-10-12 14:11:58 +05:30
Byson94
6d32f4f823 fix: even more fixing of gtk4 errors 2025-10-12 14:11:58 +05:30
Byson94
22f9e7e272 fix: fixing the drag thingy in eventbox 2025-10-12 14:11:58 +05:30
Byson94
e73173888c fix: fixing broken wifi gui manager screenshot 2025-10-12 14:11:58 +05:30
Byson94
faf8afdd65 feat: added name in 0.2.0 release changelog 2025-10-12 14:11:58 +05:30
Byson94
f2fc0ca891 fix: fixing more errors that i cant name of 2025-10-12 14:11:58 +05:30
Byson94
e7346cb28a fix: fixing many many more gtk4 migration errs 2025-10-12 14:11:58 +05:30
Byson94
bbc99df84d fix: added controlflow in gif render part 2025-10-12 14:11:58 +05:30
Byson94
cf0367e069 fix: fixing more gtk4 related err 2025-10-12 14:11:58 +05:30
Byson94
1f1b1b2b01 feat: added EventControllerKey in widget_definition.rs 2025-10-12 14:11:58 +05:30
Byson94
a6841935cc fix: fixing gtk_layer_shell naming issues 2025-10-12 14:11:58 +05:30
Byson94
96039a47aa fix: fixing glib weak reference issues 2025-10-12 14:11:58 +05:30
Byson94
2d30974f85 fix: fixing timeouts in eventbox being a borrow 2025-10-12 14:11:57 +05:30
Byson94
c5949be319 feat(rewrite): button and event_box widgets 2025-10-12 14:11:44 +05:30
Byson94
f21833b641 feat: rewriting eventbox to work with gtk4 2025-10-12 14:11:44 +05:30
Byson94
35cb12b2f2 fix: fixing even more errors in widget_definition 2025-10-12 14:11:44 +05:30
Byson94
4efd34f78b fix: fixing glib::clone! in widget_definitions.rust 2025-10-12 14:11:44 +05:30
Byson94
d35161d5d1 fix: fixed connect_monitor_added function 2025-10-12 14:11:44 +05:30
Byson94
fd73225688 fix: fixed style_provider reelated err 2025-10-12 14:11:44 +05:30
Byson94
b7b7f15420 fix: more errors and follow new gtk4 model 2025-10-12 14:11:22 +05:30
Byson94
b910eb741a feat: stubbing widget creation logic for easier migration 2025-10-12 14:11:22 +05:30
Byson94
fec83417f5 wip: migrate to gtk4 2025-10-12 14:11:10 +05:30
Byson94
b3f2c3f8a9 chore: a small rename :) 2025-10-12 14:11:10 +05:30
Byson94
1eaf773206 feat: removed monitor lib in favour of gtk4 2025-10-12 14:11:10 +05:30
Byson94
38222328c2 feat: fixed falure send & added print flag to engine-override 2025-10-12 14:11:10 +05:30
Byson94
dc04096587 feat: better management of rhai crate 2025-10-12 14:11:10 +05:30
Byson94
52182d3b4e feat: removed more code/dependencies 2025-10-12 14:11:10 +05:30
Byson94
c59f3f3683 feat: removed unnecessary dependencies 2025-10-12 14:11:10 +05:30
Byson94
348b4bcf98 chore: cargo fmt 2025-10-12 14:11:10 +05:30
Byson94
f811704e5e feat: added file path indicator in errors! 2025-10-12 14:11:10 +05:30
Byson94
c2867b6fc8 fix: resolver throwing err at import on p/e err 2025-10-12 14:11:10 +05:30
Byson94
972a5bb357 feat: even better err support with rhai_trace v3 2025-10-12 14:11:10 +05:30
Byson94
eb73c5b08f feat: renaming setup_for_rt to setup_dyn_ids 2025-10-12 14:11:10 +05:30
Byson94
53607bf019 feat: removed INPUT_VAL var support in input wdgt 2025-10-12 14:11:10 +05:30
Byson94
21216b642c feat: improved the module heading generation 2025-10-12 14:11:10 +05:30
Byson94
39c284682a fix: fixed doc error 2025-10-12 14:11:10 +05:30
Byson94
5d367195ed chore: removed lagacy support on focusable win prop 2025-10-12 14:11:10 +05:30
Byson94
b38ed83289 feat: added force_normal property for windows 2025-10-12 14:11:10 +05:30
Byson94
22ae76c2ce fix: m/min duration end & ewwii crash 2025-10-12 14:11:10 +05:30
Byson94
1ff85fef2f fix: fixed broken m value for min 2025-10-12 14:11:10 +05:30
Byson94
160ec69391 feat: improving the engine-override feature 2025-10-12 14:11:10 +05:30
Byson94
a97e5fb762 fix(typo): in 0.2.0 CHANGELOG 2025-10-12 14:11:10 +05:30
Byson94
98214786d8 feat: added engine-override command 2025-10-12 14:11:10 +05:30
Byson94
01e6f2d6b8 feat(stdlib): added std::regex for regex matching 2025-10-12 14:11:10 +05:30
Byson94
9a92276bd0 chore: removed added support for open-many in v0.2.0 2025-10-12 14:11:10 +05:30
Byson94
d491a76c43 feat: improved rhai docs generator 2025-10-12 14:11:10 +05:30
Byson94
9592f9333a fix(generate-rhai-docs): broken code for docusaurus 2025-10-12 14:10:50 +05:30
Byson94
fda4b68c56 feat: remove stuff in autogen 2025-10-12 14:10:20 +05:30
Byson94
317b6162ea feat(generate-rhai-docs): fix mdx issues 2025-10-12 14:10:07 +05:30
Byson94
5dc6f3be30 feat: renaming js => javascript 2025-10-12 14:08:30 +05:30
Byson94
a999cc8948 feat: remove docs/ and moving to docusarus 2025-10-12 14:08:21 +05:30
Byson94
ef700d9d85 feat: added m as another duration unit for minute 2025-10-12 14:08:21 +05:30
Byson94
bd6e540d04 feat: more renaming | eww => ewwii 2025-10-12 14:08:21 +05:30
Byson94
68e0f9e35a feat: removing a few comment of minee 2025-10-12 14:08:21 +05:30
Byson94
9e3fcf2e09 feat: fully renamed EwwPaths => EwwiiPaths 2025-10-12 14:08:20 +05:30
Byson94
34019e65ba feat: made eval_code fn use the compile_code fn to compile 2025-10-12 14:08:20 +05:30
Byson94
df3fb81b6a chore: renaming a few eww => ewwii 2025-10-12 14:08:20 +05:30
Byson94
4fabb49ed3 feat: added state command and fixed log issues 2025-10-12 14:08:20 +05:30
Byson94
7db5a1305c feat: added --preserve in the docs 2025-10-12 14:08:20 +05:30
Byson94
14b5cff081 feat: added date in 0.1.4 release 2025-10-12 14:08:20 +05:30
Byson94
89bb47e53e feat: added perserve flag to the update command which preserves the new updates 2025-10-12 14:08:20 +05:30
Byson94
14b54e7790 chore(cargo): building for 0.1.3 2025-10-12 14:08:20 +05:30
Byson94
b0044d6f0a feat: fixed image_width and height not working issue for image 2025-10-12 14:08:20 +05:30
Byson94
c0f1a2e1c8 feat: removed getting started section from docs 2025-10-12 14:08:20 +05:30
Byson94
50d95f1870 chore: fixing a comment... 2025-10-12 14:08:20 +05:30
Byson94
e4187a6d4c feat: reworking the inject-vars arg of update 2025-10-12 14:08:20 +05:30
Byson94
b5a639936b feat: made update cmd so that it will preserve current wdgt state 2025-10-12 14:08:20 +05:30
Byson94
4ca6716074 feat(docs): added exclusive true in config 2025-10-12 14:08:20 +05:30
Byson94
1937508781 fix(docs): fixed invalid config in configuration.md 2025-10-12 14:08:20 +05:30
Byson94
37be5104b2 feat: updating ewwii dark theme 2025-10-12 14:08:20 +05:30
Byson94
976f0b4e34 feat: improved parent-death sig on freebsd and linux 2025-10-12 14:08:20 +05:30
Byson94
ab1b2a8a96 feat: removed todo 2025-10-12 14:08:20 +05:30
Byson94
2081d13b81 chore: cargo formatting 2025-10-12 14:08:20 +05:30
Byson94
164b7858f7 feat: added date to 0.1.1 release in changelog 2025-10-12 14:08:20 +05:30
Byson94
fb528adeb9 feat: migrated to rhai_trace 0.2.0 2025-10-12 14:08:20 +05:30
Byson94
4aecfa43ab perf: improved poll handling and made it reusable 2025-10-12 14:08:20 +05:30
Byson94
6bc8822e04 feat: made update not require a window argument 2025-10-12 14:08:20 +05:30
Byson94
095e99bc0a feat: fix poll/listen not working for multiple window 2025-10-12 14:07:28 +05:30
Byson94
623a79da54 feat: removed unused deps 2025-10-12 14:07:16 +05:30
Byson94
9e9d57be8e feat: fixing documentation mistake which said main.rhai as main file 2025-10-12 14:07:16 +05:30
Byson94
cbe77b3b1f feat: adding release date of 0.1.0 2025-10-12 14:07:16 +05:30
Byson94
aff9a999b0 feat(theme): improved docs ewwii theme 2025-10-12 14:07:16 +05:30
Byson94
588f843e0c feat: updated wifi gui template image 2025-10-12 14:07:16 +05:30
Byson94
0957ef2804 chore: Update README.md 2025-10-12 14:07:06 +05:30
Byson94
c571bfb03b feat: removed exit on config error mechanism of daemon 2025-10-12 14:07:06 +05:30
Byson94
d7445dabb2 fix: fixed dyn var extractor not skipping comments issue 2025-10-12 14:07:06 +05:30
Byson94
1dac50f70a fix: fixed broken call-fns command 2025-10-12 14:07:06 +05:30
Byson94
3a04893caa fix: fixed default poll/listen variables not working in external modules 2025-10-12 14:07:06 +05:30
Byson94
83c292de6b feat: improved both resolver failed error 2025-10-12 14:07:06 +05:30
Byson94
a2e18ee7df feat: fixed mod resolver not printing error issue 2025-10-12 14:07:06 +05:30
Byson94
84c455f557 feat: many internal changes regarding enter(..)
1. Made it so that there wont be any error if user didnt return anything
2. Added support for defining multiple enter(..)
3. Added support for merging multiple enter(..)
2025-10-12 14:07:06 +05:30
Byson94
22981292cc feat: better errors with rhai_trace (lib built for ewwii)
With this commit, Ewwii now integrates rhai_trace, which is a rust library that I
built to generate better errors with rhai.

It is plugged into codespan reporting for providing awesome errors which
makes programming with ewwii much easier! It is a major UX update along
with the removal of `dyn_id` dependance.
2025-10-12 14:07:06 +05:30
Byson94
4d0d84c44d docs: improved the doc flow 2025-10-12 14:07:06 +05:30
Byson94
b9d5fd8e68 feat: added TODO.md 2025-10-12 14:07:06 +05:30
Byson94
826d560b84 feat: improved the removed dyn_id part in changelog 2025-10-12 14:07:06 +05:30
Byson94
5b47b67c83 feat(MAJOR): removed the need for dyn_id
This is a very very very big update for UX! Ewwii finally has support
for automatically assigning `dyn_id`.

This was actually not as hard as I thought! I just had to mutate the
widget AST and inject a `dyn_id` in based on its parent.

It works soooo well and the burden on the user just reduced sooo much!
2025-10-12 14:07:06 +05:30
Byson94
0561ecba3f feat(docs): added wifi_manager template 2025-10-12 14:07:06 +05:30
Byson94
c9631268bd feat(rename): renamed iirhai --> rhai_impl 2025-10-12 14:07:06 +05:30
Byson94
11510bd8b4 feat(github): added module request issue template 2025-10-12 14:06:39 +05:30
Byson94
a58694a04d feat: renamed widgetnode.rs to ast.rs in iirhai 2025-10-12 14:06:39 +05:30
Byson94
9667a73b05 feat: removed modules example 2025-10-12 14:06:39 +05:30
Byson94
24b57fb269 feat: updated readme contribewwtiing steps 2025-10-12 14:06:39 +05:30
Byson94
cf1dc459a2 feat: removed statictranspl docs 2025-10-12 14:06:39 +05:30
Byson94
306c4c542b style: remove pre_description having \t at start 2025-10-12 14:06:05 +05:30
Byson94
61bb98460d feat: added contributing.md 2025-10-12 14:03:22 +05:30
Byson94
53116df6a0 feat: added api::linux library for rhai 2025-10-12 14:03:16 +05:30
Byson94
29c70d26c7 feat: remove unused stuff and do namespace changes 2025-10-12 14:03:01 +05:30
Byson94
ee40a2265a fix: borken % based w/h in window def 2025-10-12 14:02:41 +05:30
Byson94
167df98182 style: fixed pre_description having \t at the start 2025-10-12 14:02:41 +05:30
Byson94
904ceb8f09 feat(ci): added gh-page publishment on changes in generate-rhai-docs/** 2025-10-12 14:02:41 +05:30
Byson94
307ba2fe61 feat: added feature for adding descriptions in generated mod docs 2025-10-12 14:02:41 +05:30
Byson94
87b615999d chore: ran cargo fmt 2025-10-12 14:02:41 +05:30
Byson94
1e8ab7f983 fix(ci): added necessary dependencies that need to be installed 2025-10-12 14:02:41 +05:30
Byson94
2293993865 fix(ci): fixed broken rust install 2025-10-12 14:02:41 +05:30
Byson94
69bf08729d fix(ci): outdated cache using v2 2025-10-12 14:01:47 +05:30
Byson94
4fa04a4b6e feat(ci): added module docs generation logic in workflow 2025-10-12 14:01:47 +05:30
Byson94
0572480239 feat: finished writing apilib docs 2025-10-12 14:01:47 +05:30
Byson94
092ecb407d wip: Massive doc automation and improvement. 2025-10-12 14:01:47 +05:30
Byson94
e63886b402 feat: improved poll/listen handler setup/shutdown in ewwii open/close cmd 2025-10-12 14:01:47 +05:30
Byson94
4b18b6c4f1 feat: DID MASSIVE improvements and changes that i cant summarize
So basically, i added sigint/sigterm catching to kill all children cleanly, then i fixed the std::monitor module as it returned tuple which didnt work with rhai much, and then i removed the deprecated attribute warning thingy. Yeah thats it.
2025-10-12 14:01:47 +05:30
Byson94
678c17c80f docs: fixed update docs 2025-10-12 14:01:47 +05:30
Byson94
076cba0c85 docs: moved advanced commands below examples 2025-10-12 14:01:47 +05:30
Byson94
f9beb589db docs: improved variables config documentation 2025-10-12 14:01:47 +05:30
Byson94
3f439a2aa0 docs: improved docs by adding commands/ section 2025-10-12 14:01:47 +05:30
Byson94
b8927722aa fix: removed kill_state_change_handler() from close_window bcz it causes issues 2025-10-12 14:01:47 +05:30
Byson94
a0945ecc84 feat: added rhai to workspace 2025-10-12 14:01:47 +05:30
Byson94
e51bdc3647 perf: replaced Arc<> with Rc<> in single threaded instances 2025-10-12 14:01:47 +05:30
Byson94
a6b87183e1 feat: added good syntax highlighting for ewwii theme 2025-10-12 14:01:47 +05:30
Byson94
a491077fca fix: fixed zombie process creation due to 100ms read time 2025-10-12 14:01:47 +05:30
Byson94
2a369d0532 feat: introduced 16ms (1 frame) batching for ewwii 2025-10-12 14:01:47 +05:30
Byson94
e83239af24 feat: fixed [NoBackend] not existing 2025-10-12 14:01:47 +05:30
Byson94
34a4664499 feat: set release date of 0.1.0-beta as today 2025-10-12 14:01:47 +05:30
Byson94
1606c61745 fix: fixed slider updating value while dragging issue 2025-10-12 14:01:47 +05:30
Byson94
ade3c45be5 feat: made listen similar to eww's model to avoid kill issues 2025-10-12 14:01:47 +05:30
Byson94
7efda1ba42 feat: added kill_state_change_handler call in stop_application 2025-10-12 14:01:47 +05:30
Byson94
25c9890fe3 fix: fixed warnings on child 2025-10-12 14:01:47 +05:30
Byson94
baf8c71909 fix: preventing early terminiation of listen child 2025-10-12 14:01:47 +05:30
Byson94
369d75af26 perf: improved perf in ewwii_config.rs by decreaasing amount of clone 2025-10-12 14:01:47 +05:30
Byson94
cf66eb8853 perf: moved from deep cloning/owning to borrowing data 2025-10-12 14:01:47 +05:30
Byson94
f714c9211d fix: removed rhai full optimization as it caused issues 2025-10-12 14:01:47 +05:30
Byson94
2d34db53fe style: made widget of template1 400 2025-10-12 14:01:47 +05:30
Byson94
a7f0065e85 feat: added templates section in README 2025-10-12 14:01:47 +05:30
Byson94
8fdb800dea docs: added proper site url to book.toml 2025-10-12 14:01:47 +05:30
Byson94
a747c5baba Stop tracking docs/book 2025-10-12 14:01:47 +05:30
Byson94
b4238e06b3 feat(license): make eww/ewwii licensing more clear 2025-10-12 14:01:33 +05:30
Byson94
2d2e288f5c perf: added full rhai optmization for speed 2025-10-12 13:59:49 +05:30
Byson94
1326716b0b feat: upgraded to rust 1.89.0, fixed new compiler warnings 2025-10-12 13:59:49 +05:30
Byson94
a6254b4308 docs(style): added ewwii (dark) theme that matches starlight theme 2025-10-12 13:59:49 +05:30
Byson94
f1e2e9561c docs(style): set navy as default dark theme 2025-10-12 13:59:49 +05:30
Byson94
1d5f3bbecd feat: replaced rust,ignore with js 2025-10-12 13:59:49 +05:30
Byson94
a435cb8a20 feat: replaced default theme with catuppuccin 2025-10-12 13:59:49 +05:30
Byson94
d3d0c59a80 docs: added approximation symbol to equations using pi (float num) 2025-10-12 13:59:49 +05:30
Byson94
f947c97b83 feat: improved wifi apilib and fixed prop conversion errors 2025-10-12 13:59:49 +05:30
Byson94
5d53c08fd7 feat: added parse error handling for simplefileresolver 2025-10-12 13:59:49 +05:30
Byson94
cb52e7047c feat: added INPUT_VAL env comamnd to props of input widget 2025-10-12 13:59:49 +05:30
Byson94
23a7d22062 feat: added std::command module to rhai 2025-10-12 13:59:49 +05:30
Byson94
af7cf437a3 fix: fixed stored_widget_node not existing on first run issue 2025-10-12 13:59:49 +05:30
Byson94
adfa2d9c58 feat: added update and call-fns command to ewwii 2025-10-12 13:59:49 +05:30
Byson94
62d6092956 feat: added pretty logging for rhai ParseError 2025-10-12 13:59:49 +05:30
Byson94
84bd33bfd6 docs: statictranspl --> staticscript 2025-10-12 13:59:49 +05:30
Byson94
9b13f4cea7 docs: added section for user defined modules in ewwii docs module section 2025-10-12 13:59:49 +05:30
Byson94
e2614a9832 feat: made ewwii print errors from external modules 2025-10-12 13:59:49 +05:30
Byson94
d1ecb16823 feat: added dyn_id property to hash props test fn 2025-10-12 13:59:49 +05:30
Byson94
e21d8fd836 pref: made WidgetInfo borrow data instead of owning it to improve perf 2025-10-12 13:59:49 +05:30
Byson94
2ffac9e6ca pref: removed .clone() of props from WindowInitiator::new() 2025-10-12 13:59:49 +05:30
Byson94
9de5181aaa perf: made root_node Arc<WidgetNode> to safe resources | ewwii_config.rs 2025-10-12 13:59:49 +05:30
Byson94
e35477aecc feat: removed rc wrapping form store as store was already wrapped in arc 2025-10-12 13:59:49 +05:30
Byson94
9930241970 perf: wrapped compiled_ast in arc to avoid data clone 2025-10-12 13:59:49 +05:30
Byson94
494a8da3f7 feat: removed instant::now() tracking on re-eval sys 2025-10-12 13:59:49 +05:30
Byson94
aa25c4735e perf: made store a rc to avoid clones | app.rs:371 2025-10-12 13:59:49 +05:30
Byson94
1d260f65bc perf: cached parser in re-eval system for speed 2025-10-12 13:59:49 +05:30
Byson94
3c6cbe1638 feat: improved runtime error handling of widgetnode casting 2025-10-12 13:59:49 +05:30
Byson94
fa63a99ae3 feat: reduced max_width to 100 and ran cargo fmt 2025-10-12 13:59:49 +05:30
Byson94
da1c22f525 perf: reusing compiled config to save performance and time 2025-10-12 13:59:49 +05:30
Byson94
70bbb630cc feat: stopped setting homogeneous in space_even prop is not provided 2025-10-12 13:59:49 +05:30
Byson94
44814071c8 feat: removed seen_widget_ids logic in diffing system 2025-10-12 13:59:49 +05:30
Byson94
d3a8f22bfb feat: added propagate_natural_height property to scrolledwindow 2025-10-12 13:59:49 +05:30
Byson94
43b31cb814 feat: added trace log lvel support 2025-10-12 13:59:49 +05:30
Byson94
40d5972533 feat: trace can be enabled if EWWII_TRACE env var exists 2025-10-12 13:59:49 +05:30
Byson94
26329aa75f feat: updated docs to be up-to-date with the latest changes 2025-10-12 13:59:49 +05:30
Byson94
4b698f48fa feat: made resolve_rhai_widget_attrs not rely on widgetnode -- less work for maintainers 2025-10-12 13:59:49 +05:30
Byson94
786ebf04d4 chore: ran cargo fmt 2025-10-12 13:59:49 +05:30
Byson94
9fbfcca1c2 feat: added std::math lib for rhai 2025-10-12 13:59:49 +05:30
Byson94
daf11c8d1a chore: ran cargo fmt 2025-10-12 13:59:49 +05:30
Byson94
28809ff783 fix: fixed reordering issues in dyn system
Fixed the reodering issue in the dynamic update system which was caused
by the use of `pack_end()` for adding a child to box_container in the create_widget() fn.

`pack_end()` is a horrible option as it would require hardcoding
"expanded", "fill" etc.

This fix uses the better option `add()` which fixes all the reodering
issues.

According to all the tests, (did 15+ tests in a dyn fn that would return 3
widgets) non of them had ordering issues. So, I will mark this as a fix.
2025-10-12 13:59:48 +05:30
Byson94
48dbdeb5c6 feat: made dyn sys a lil better by obliterating redundant wdgts 2025-10-12 13:59:48 +05:30
Byson94
09bba44229 feat: added better error handling for fnnotdefined 2025-10-12 13:59:48 +05:30
Byson94
6e8207cb56 feat: made window resizable by default. 2025-10-12 13:59:48 +05:30
Byson94
557254f74a refactor: removed unnecessary println! in app 530 2025-10-12 13:59:48 +05:30
Byson94
3a72ddf3d7 fix: fixed stdlib shadowed by apilib issue 2025-10-12 13:59:48 +05:30
Byson94
043892d23b feat: made x,y,widget,height props optional 2025-10-12 13:59:48 +05:30
Byson94
0490604277 feat: added get_adapter_connectivity() fn to api::wifi 2025-10-12 13:59:48 +05:30
Byson94
584a6e5911 chore: ran cargo fmt 2025-10-12 13:59:48 +05:30
Byson94
049a0337b2 feat: fixed broken current_connection_linux command 2025-10-12 13:59:48 +05:30
Byson94
a42cc5df37 chore: added new feats to changelog.md 2025-10-12 13:59:48 +05:30
Byson94
4d29933271 feat: added api module to rhai 2025-10-12 13:59:48 +05:30
Byson94
00ba7af473 feat: added json module to rhai 2025-10-12 13:59:48 +05:30
Byson94
f2a177bc79 feat: added fix in changelog.md 2025-10-12 13:59:48 +05:30
Byson94
04aff76ea4 chore: ran cargo fmt 2025-10-12 13:59:48 +05:30
Byson94
6ff1c07c42 fix: fixed window reopening issue after closing it. 2025-10-12 13:59:48 +05:30
Byson94
c23d0d29dd feat: added kill switch for poll/listen 2025-10-12 13:59:48 +05:30
Byson94
e8d08f8c43 style: removed redundant , in readme 2025-10-12 13:59:48 +05:30
Byson94
9b6f310d4f feat: added data struct example in readme 2025-10-12 13:59:48 +05:30
Byson94
f281396b6f feat: removed .yuck files from ewwii examples 2025-10-12 13:59:48 +05:30
Byson94
95ebf251a2 chore: ran cargo fmt 2025-10-12 13:59:48 +05:30
Byson94
a14163bfca feat: removed root_widget param of update_widget_tree() 2025-10-12 13:58:36 +05:30
Byson94
c64d4e5cb5 fix: removed unused id computation in get_id_to_widget_info() 2025-10-12 13:58:36 +05:30
Byson94
9bd70df8bf feat: did some stuff that could possible make create widget work 2025-10-12 13:58:36 +05:30
Byson94
8f68cc6abc chore: ran cargo fmt 2025-10-12 13:58:36 +05:30
Byson94
59709c3ddf feat: made diffing sys work | possibly
The diffing system works with updates -- as seen from tests but the widget removal/creation system is still untested and may still need more iterations.
2025-10-12 13:58:36 +05:30
Byson94
405e50effb fix: fixed widget.clone() errors 2025-10-12 13:58:36 +05:30
Byson94
631809af3c feat: added a better diffing system 2025-10-12 13:58:36 +05:30
Byson94
90f95c2963 feat: got a minimal prototype of the diffing system 2025-10-12 13:58:36 +05:30
Byson94
c252d2d554 feat: fixed broken get_home_dir() fn in std::env | rhai 2025-10-12 13:58:36 +05:30
Byson94
fdb1697ead feat: added example section and section for statctranspl 2025-10-12 13:58:36 +05:30
Byson94
045784f1f9 chore: ran cargo fmt 2025-10-12 13:58:36 +05:30
Byson94
ef13f128ed feat: fixed all compilation warnings 2025-10-12 13:58:36 +05:30
Byson94
ef46ac9d0e feat: added dyn prop support to all widgets i thnk 2025-10-12 13:58:36 +05:30
Byson94
b430e4929b feat: added kill_on_drop for listener 2025-10-12 13:58:36 +05:30
Byson94
831fe185e3 feat: added prop dyn support to more widgets 2025-10-12 13:58:36 +05:30
Byson94
fdb9c08895 feat: replaced byson94 -> Ewwii-sh 2025-10-12 13:58:36 +05:30
Byson94
abe0a37aca fix: fixed doc name and replaced eww with ewwii 2025-10-12 13:58:36 +05:30
Byson94
9f86286e57 feat: added home-button to docs 2025-10-12 13:58:36 +05:30
Byson94
9ada74d70b feat: added docs back in 2025-10-12 13:58:36 +05:30
Byson94
855f59b407 feat: replaced apache-2.0 with gplv3 2025-10-12 13:58:35 +05:30
Byson94
edcdbcc577 feat: apache-2.0 to gpl3. Making ewwii fully open source. 2025-10-12 13:58:35 +05:30
Byson94
a24daad15e fix: removed redundant argument from fn which caused errors 2025-10-12 13:58:35 +05:30
Byson94
203f5418fa feat: moved docs to Ewwii-sh/docs 2025-10-12 13:58:35 +05:30
Byson94
30c37a1fa4 chore: run cargo fmt 2025-10-12 13:58:35 +05:30
Byson94
863bfb5cf4 feat: added modules example 2025-10-12 13:58:35 +05:30
Byson94
f8bc0835d0 feat: Added monitor stdlib 2025-10-12 13:58:35 +05:30
Byson94
bef32197da feat: added data-structs example 2025-10-12 13:58:35 +05:30
Byson94
1b08627c44 Replaced example screenshot 2025-10-12 13:58:35 +05:30
Byson94
501cf8869d chore: ran cargo fmt 2025-10-12 13:58:35 +05:30
Byson94
5d272ce420 feat: added example, fixed small bugs 2025-10-12 13:58:35 +05:30
Byson94
e15762c725 docs: added dyn_id docs 2025-10-12 13:58:35 +05:30
Byson94
a8f54306aa hell: I AM SORRY! I CAUSED PAIN UPON ALL USERS
I MADE KEYS A REQUIRED FACTOR FOR DYNAMIC UPDATES.... I AM REALLY SORRY, I HAD NO OTHER OPTIONS OTHAT THAN THIS MAJOR UX FLAW...
2025-10-12 13:58:35 +05:30
Byson94
664412553d feat: Added dynamic prop update support 2025-10-12 13:58:35 +05:30
Byson94
b512a35e3d feat: added recursive support for get_id_to_props_map 2025-10-12 13:58:35 +05:30
Byson94
6e3268ca17 feat: implmented half working dynamic update system 2025-10-12 13:58:35 +05:30
Byson94
03a2bcf37e feat: Build custom module resolver for rhai 2025-10-12 13:58:35 +05:30
Byson94
6f65d127aa docs: rearranged expr_lang.md path 2025-10-12 13:58:35 +05:30
Byson94
1034c44ee3 docs: readded expression_language, fixed str interpolation eg 2025-10-12 13:58:35 +05:30
Byson94
4ec6fe308c fix: replaced include() fn in config doc as it got removed 2025-10-12 13:58:35 +05:30
Byson94
01b05b0bda fix: fixed first window example in config::configuration.md 2025-10-12 13:58:35 +05:30
Byson94
d377fc409f feat: Added Widget Properties and widget param doc
Added widget properties (what they do & their signature), the parameters of the widget function call.

I may have also improved the docs on some other parts which I may have forgotten.
2025-10-12 13:58:35 +05:30
Byson94
c2c0c7e292 feat: removed include because rhai supports import/export 2025-10-12 13:58:35 +05:30
Byson94
8609a6acb7 feat: Added example 2025-10-12 13:58:35 +05:30
Byson94
7142567848 chore: ran cargo fmt 2025-10-12 13:58:35 +05:30
Byson94
58c93576b2 feat: Added support for stack widget 2025-10-12 13:58:35 +05:30
Byson94
5a4972f940 feat: Added transform widget support. 2025-10-12 13:58:35 +05:30
Byson94
64f94a11cd feat: Added tooltip, clrchooser, clrbttn widget support 2025-10-12 13:58:35 +05:30
Byson94
45745ed381 chore: ran cargo fmt 2025-10-12 13:58:35 +05:30
Byson94
6834b664fd feat: Added basic and fragile dyn sys for poll/listen 2025-10-12 13:58:35 +05:30
Byson94
f03fbbfafe refactor: Fixed many warnings 2025-10-12 13:58:35 +05:30
Byson94
ef52f691cf chore(rebrand): replaced eww to ewwii 2025-10-12 13:58:23 +05:30
Byson94
273fdfb082 feat: Added compile_code fn to parser 2025-10-12 13:58:23 +05:30
Byson94
1de8c6998a doc: Added modules documentation 2025-10-12 13:58:23 +05:30
Byson94
cc21ff1863 feat: Added std module with text and env submodule 2025-10-12 13:58:23 +05:30
Byson94
ea0f4d091a feat: added better dynamic update 2025-10-12 13:58:23 +05:30
Byson94
4a43eabed2 docs: fixed config & syntax file path 2025-10-12 13:58:23 +05:30
Byson94
9d18de1110 docs: improved docs and added nested chapters 2025-10-12 13:58:23 +05:30
Byson94
87984cdaa8 docs: improved docs by refactoring the configuration.md 2025-10-12 13:58:23 +05:30
Byson94
ab5d8ce28a feat: added gtk event box support 2025-10-12 13:58:23 +05:30
Byson94
118af53cc8 feat: added gtk scrolledwindow support 2025-10-12 13:58:23 +05:30
Byson94
d7564d716f feat: Added gtk calendar support 2025-10-12 13:58:23 +05:30
Byson94
69d8523b85 chore: ran cargo fmt 2025-10-12 13:58:23 +05:30
Byson94
535cf3b3ba feat: added gtk input support and changed optional logic 2025-10-12 13:58:23 +05:30
Byson94
ddaffd166a feat: added support for gtk image 2025-10-12 13:58:23 +05:30
Byson94
7bec8e42ce feat: added support for graph 2025-10-12 13:58:23 +05:30
Byson94
5e3f538674 feat: added support for circular progress bar 2025-10-12 13:58:23 +05:30
Byson94
530ccca1e4 feat: added support for gtk expander 2025-10-12 13:58:23 +05:30
Byson94
f1a7cfb48c feat: added support for gtk_combo_box_text 2025-10-12 13:58:23 +05:30
Byson94
6b9fec8ef7 chore: removed unnecessary test 2025-10-12 13:58:23 +05:30
Byson94
322311bda3 chore: fixed logo path 2025-10-12 13:58:23 +05:30
Byson94
643db859a3 chore: Changed logo 2025-10-12 13:58:23 +05:30
Byson94
e374a203ff chore: ran cargo fmt 2025-10-12 13:58:23 +05:30
Byson94
21658b19bc feat: Added support for gtk revealer widget 2025-10-12 13:58:23 +05:30
Byson94
d6b439f097 feat: added checkbox widget support 2025-10-12 13:58:23 +05:30
Byson94
74c6be70f7 feat: Added basic props support for all widgets 2025-10-12 13:58:23 +05:30
Byson94
8dd99376fd feat: Added semi dynamic support for rhai 2025-10-12 13:58:23 +05:30
Byson94
653bf17535 chore: run cargo fmt 2025-10-12 13:58:23 +05:30
Byson94
4b100d0073 feat: Added note section in error report 2025-10-12 13:58:23 +05:30
Byson94
5a0d267770 chore: ran cargo fmt to format code 2025-10-12 13:58:23 +05:30
Byson94
adb0442da5 feat: Improved err printing with help/hint and better UI 2025-10-12 13:58:23 +05:30
Byson94
674735ca65 feat: added dummy variables in rhai scope to fix eval 2025-10-12 13:58:23 +05:30
Byson94
ae31a360e9 feat: Added poll/listen IMPROVED error printing 2025-10-12 13:58:23 +05:30
Byson94
2188e9000e feat: Added polling (untested) 2025-10-12 13:58:23 +05:30
Byson94
1623d208f7 feat: starting the work on rhai signals 2025-10-12 13:58:22 +05:30
Byson94
18e9c91d69 fix: fixed license in Cargo.toml(s) 2025-10-12 13:58:22 +05:30
Byson94
2dd0850854 docs: Improved docs, added more files to fill in with content 2025-10-12 13:58:22 +05:30
Byson94
65f6e436f3 feat: Added support for buttons 2025-10-12 13:58:22 +05:30
Byson94
e07b15cd00 perf: Fixed calling get_window_rectangle in a loop 2025-10-12 13:58:22 +05:30
Byson94
7de16ec735 feat: added gtk scale support 2025-10-12 13:58:22 +05:30
Byson94
f18d4f1915 feat: Added better errors for rhai 2025-10-12 13:58:22 +05:30
Byson94
855393e137 feat: Added progress bar support 2025-10-12 13:58:22 +05:30
Byson94
7488330980 feat: Added center box widget support 2025-10-12 13:58:22 +05:30
Byson94
3ee3e1da5c feat: Removed ipc_server concept entirely 2025-10-12 13:58:22 +05:30
Byson94
ce48913e43 feat(style): Added scss/css styling support for rhai 2025-10-12 13:58:22 +05:30
Byson94
c9faec6c37 feat: Removed ipc server of rhai temporarely 2025-10-12 13:58:22 +05:30
Byson94
79041e5f40 feat: supported creating gtk labels in rhai 2025-10-12 13:58:22 +05:30
Byson94
5d03bdf561 fix: removed child of button and moved it to props 2025-10-12 13:58:22 +05:30
Byson94
8359d2191a fix: replaced ewwii.lua -> ewwii.rhai 2025-10-12 13:58:22 +05:30
Byson94
2f0874531e docs: Improved structure and moved installation to getting started 2025-10-12 13:58:22 +05:30
Byson94
b43cdabd46 Updated todo 2025-10-12 13:58:22 +05:30
Byson94
4aee3477a5 Added pretty_assertions to Cargo.toml 2025-10-12 13:58:22 +05:30
Byson94
2f027e5fa1 refactor: formatted code with cargo fmt 2025-10-12 13:58:22 +05:30
Byson94
b6df3aaf38 fix: Removed ParseError which was a duplicate of Error 2025-10-12 13:58:22 +05:30
Byson94
dace5d545a feat: fixed backend win opts to include fn for most structs 2025-10-12 13:58:22 +05:30
Byson94
0f07854917 fix: used resizable, stacking and backend_opts directly in win init 2025-10-12 13:58:22 +05:30
Byson94
8efaec8c6d feat: Added backend_options in windowdef struct 2025-10-12 13:58:22 +05:30
Byson94
ed5e388e7d feat(changelog): added new point in changelog 2025-10-12 13:58:22 +05:30
Byson94
11d1f3f1ce feat(ci): removed deno usage in gh-pages.yml 2025-10-12 13:58:22 +05:30
Byson94
0b5d69bf28 fix(ci): Fixed path (eww -> ewwii) 2025-10-12 13:58:22 +05:30
Byson94
636e926ece fix: Fixed monitoridentifier using index instead of numeric 2025-10-12 13:58:22 +05:30
Byson94
c57248363e feat: Added fromstr for anchorpoint 2025-10-12 13:58:22 +05:30
Byson94
4f88a2e554 feat: Added from fn to convert win_geo::coords to coords::Coords 2025-10-12 13:58:22 +05:30
Byson94
4b13c38c02 fix: Did many fixes that i forgot | 17 errors remaining 2025-10-12 13:58:22 +05:30
Byson94
c826822ea5 fix: removed .ok() from prop code in widget_def.rs 2025-10-12 13:58:22 +05:30
Byson94
ed204b9111 fix: used stm_enum_parse! in backend win opts instead of enum_parse! 2025-10-12 13:58:22 +05:30
Byson94
2f05508385 fix: fixed invalid use of diagerror & removed unnecessary arg in opts 2025-10-12 13:58:22 +05:30
Byson94
1108b33f7c fix: renamed window_geometry align_coord fn to work with app.rs 2025-10-12 13:58:22 +05:30
Byson94
31e8a8e302 feat: Removed open-many arg because we are pre-mature 2025-10-12 13:58:22 +05:30
Byson94
0eaa5f458b fix(warn): fixed all warnings related to imports 2025-10-12 13:58:22 +05:30
Byson94
d065e91626 feat(widgets): Made build_widget support both win_def and win_node 2025-10-12 13:58:22 +05:30
Byson94
eca6f74545 fix: changed fn in app.rs and fixed coords error 2025-10-12 13:58:22 +05:30
Byson94
39c08c8201 fix: removed old printstate and printgraph daemon commands 2025-10-12 13:58:22 +05:30
Byson94
01fbe1cf24 fix: removed semicolon form enum usage in backend win opts 2025-10-12 13:58:22 +05:30
Byson94
5d5d82b3d6 fix: added cstm_enum_parse to output errors in enumparserror 2025-10-12 13:58:22 +05:30
Byson94
d213e91260 docs: changed theme to navy (ayu -> navy) 2025-10-12 13:58:22 +05:30
Byson94
434cc3836a fix: naming error of anchor_point in window_geometry.rs 2025-10-12 13:58:22 +05:30
Byson94
3f3036c8cc fix: removed Eq derive from backend win opts to fix numwithunit error 2025-10-12 13:58:22 +05:30
Byson94
51a98c8e9f refactor: refactoring the code to fix the 75 errors that popped out of nowhere 2025-10-12 13:58:22 +05:30
Byson94
726e7c3ef1 fix: removed unennecessary impl in backend win opts 2025-10-12 13:58:22 +05:30
Byson94
cc7e931682 fix: fixed backend win opts and build_widget
fixed backend_window_options and build_widget codes. Removed unnecessary Error enum which used yuck internals and removed unnecessary children value in build_widget match.
2025-10-12 13:58:22 +05:30
Byson94
8c35ebdb2c fix: removed impl used by yuck in diag_error and fixed 1 compiler error 2025-10-12 13:58:22 +05:30
Byson94
00a4561a4b feat: added widget_definitions_helper file to help widget_definition codes 2025-10-12 13:58:22 +05:30
Byson94
181a5207e9 refactor: Refactored parse_geometry in window_initiator which was incomplete 2025-10-12 13:58:22 +05:30
Byson94
4814a552c7 refactor: Refactored backend_window_options and commented code related to yuck 2025-10-12 13:58:22 +05:30
Byson94
46b84fc6a8 fix: Fixed imports, lalrpop imports (not needed) and removed old yuck reference 2025-10-12 13:58:22 +05:30
Byson94
bc92ae8319 fix: uncommented error_handling_ctx to fix more compiler err 2025-10-12 13:58:22 +05:30
Byson94
d5e857f994 fix: Fixed more compiler errors (frgt what I fixed) 2025-10-12 13:58:22 +05:30
Byson94
bed41badbc fix: coord import in window_geometry 2025-10-12 13:58:15 +05:30
Byson94
c4e8692564 fix: Fixed compilation errors on WindowStacking 2025-10-12 13:58:15 +05:30
Byson94
0bef366685 fix(cargo): ewwii_tests warnings 2025-10-12 13:58:04 +05:30
Byson94
dc072e21af fix: Added proper imports (mod/use) to fix compilation errors 2025-10-12 13:58:04 +05:30
Byson94
fafb90d2d2 doc: updated changelog.md and added features and nt changes 2025-10-12 13:58:04 +05:30
Byson94
7222716957 docs(SUMMARY.md): improved sidebar layout 2025-10-12 13:58:04 +05:30
Byson94
ee172e50ed docs: Made few changes to configuration summary 2025-10-12 13:58:04 +05:30
Byson94
f6dc3dfc74 feat: Added & configuired backend_window_options a bit 2025-10-12 13:58:04 +05:30
Byson94
80b44941d0 feat: moved coords to window/ added window_geomtery from yuck/ crate 2025-10-12 13:58:04 +05:30
Byson94
73ecb64a9d feat: Added proper parse_geometry function in window_initiator.rs 2025-10-12 13:58:04 +05:30
Byson94
a8502b63b2 feat(window): Added window directory to rewrite yuck-win relation 2025-10-12 13:58:04 +05:30
Byson94
f120d12939 refactor: core window argument parser code to work with modern config parser (rhai) 2025-10-12 13:58:04 +05:30
Byson94
81abf8925b feat: adding back the files that were removed 2025-10-12 13:58:04 +05:30
Byson94
dc9aceb1cf docs(rebrand): change eww to ewwii 2025-10-12 13:57:50 +05:30
Byson94
e70685a166 fix(ci): Fixed ci to work with main branch as it used master 2025-10-12 13:57:50 +05:30
Byson94
850f322dff doc: updated docs (changed github repo) 2025-10-12 13:57:50 +05:30
Byson94
50d6e0dc1d feat(widgets): Added window.rs & fixed small compilation errs 2025-10-12 13:57:50 +05:30
Byson94
48bf133e46 feat(iirhai): Improved lable to accept props instead of string 2025-10-12 13:57:50 +05:30
Byson94
002f024926 feat(readme): Changed dependency status to ewwii's github | removed cargo_out.txt 2025-10-12 13:57:50 +05:30
Byson94
d86c0a8ba8 feat: Added test files to test build_gtk_widget() | rewriting widgets/ 2025-10-12 13:57:50 +05:30
Byson94
7603ea0371 fix: removed widget_def*.rs and fixed invalid root_widget var name in ewwii_config.rs 2025-10-12 13:57:50 +05:30
Byson94
c97ffbb99e fix(macro): Added gen_diagnostic_macro.rs to fix 15+ build errors 2025-10-12 13:57:50 +05:30
Byson94
dee74fbe03 fix(code): fixed about 10 errors due to dynval not imported 2025-10-12 13:57:50 +05:30
Byson94
4cf3a53b4f fix: Fixing more build errors that is caused by foundational scripts 2025-10-12 13:57:50 +05:30
Byson94
2b72e5ea0b fix: added root widget value to window def 2025-10-12 13:57:50 +05:30
Byson94
76c3de4ace fix(ipc): Fixed iirhai daemon to not start if it is already running 2025-10-12 13:57:50 +05:30
Byson94
a89fa7afb0 feat(nuke): removing unnecessary files which use yuck 2025-10-12 13:57:50 +05:30
Byson94
424c388ca3 style(comments): added/removed comments 2025-10-12 13:57:50 +05:30
Byson94
5f1859fff5 feat(kill): Killed state/ directory as well as files using yuck 2025-10-12 13:57:50 +05:30
Byson94
5af552721c feat(kill): killed widget_definitions which used yuck heavily 2025-10-12 13:57:50 +05:30
Byson94
3818cbc7cb feat(rm): removed yuck and simplexpr | note: you will get 444 errors 2025-10-12 13:57:50 +05:30
Byson94
31e5a695d7 fix: eww compile errors 2025-10-12 13:56:55 +05:30
Byson94
b7470581a9 feat(rename): renamed eww && eww_shared_util to be ewwii 2025-10-12 13:56:32 +05:30
Byson94
2a36c6ea6e feat(rename): Renamed eww_config to ewwii_config 2025-10-12 13:56:32 +05:30
Byson94
99fefbfb44 style(eww_config.rs): removed unnecessary whitespace 2025-10-12 13:56:32 +05:30
Byson94
f045f4eb38 feat(ipc): Improved iirhai daemon && added read/write 2025-10-12 13:56:32 +05:30
Byson94
2f130502cc feat(ipc): added mpsc to read messages from iirhai 2025-10-12 13:56:32 +05:30
Byson94
4b0f123313 feat(source): removing ALL yuck reference | NOTE: compilation will fail 2025-10-12 13:56:32 +05:30
Byson94
f69edff927 feat(ipc): Added ipc socket to paths.rs 2025-10-12 13:56:32 +05:30
Byson94
246535f3f1 fix(ipc): Fixed errors in ipc_server.rs related to iirhai 2025-10-12 13:56:32 +05:30
Byson94
efaab0c83b feat(ewwii): added functions to read iirhai ipc server 2025-10-12 13:56:32 +05:30
Byson94
669660a49c feat(ipc): added ipc daemon to iirhai 2025-10-12 13:56:32 +05:30
Byson94
3651e58631 feat(config): changed Cargo.toml files and added iirhai-daemon bin crate 2025-10-12 13:56:32 +05:30
Byson94
2ba90838e4 feat(iirhai): added ipc_manager (transpiler) 2025-10-12 13:56:32 +05:30
Byson94
72ded26393 style: just some comment ig 2025-10-12 13:56:32 +05:30
Byson94
111e0914b5 refactor: replacing yuck references with rhai 2025-10-12 13:56:32 +05:30
Byson94
2bfa24e72c rm: removed random duck/ crate 2025-10-12 13:56:32 +05:30
Byson94
bf61dd030a chore: replaced iirhai with the latest code from rhai-dlt 2025-10-12 13:56:32 +05:30
Byson94
592143137f chore(rename): rename iirhai crate 2025-10-12 13:55:58 +05:30
Byson94
999ad82e22 chore(license): rename and improve eww license 2025-10-12 13:55:30 +05:30
Byson94
1f95368168 chore: change few things and add comments 2025-10-12 13:54:49 +05:30
Byson94
bce8d05170 fix: SCREW IT! I AM DOING THIS COMMIT BECAUSE THERE WAS A MAJOR DEPENDENCY HELL 2025-10-12 13:54:49 +05:30
Byson94
7d72d4cc9c Create LICENSE 2025-10-12 13:54:49 +05:30
Byson94
3285b20d98 feat: add a basic rhai yuck replacement 2025-10-12 13:54:38 +05:30
Byson94
8cd92cc8c3 chore(rebrand): replace instance of eww with ewwii 2025-10-12 13:54:11 +05:30
Byson94
6a993f765e chore(credits): add credits in readme 2025-10-12 13:53:54 +05:30
Byson94
565fb896bd chore: mark things to change 2025-10-12 13:53:33 +05:30
Byson94
d6cff70840 chore(rebrand): replace eww with ewwii 2025-10-12 13:51:47 +05:30
Byson94
56f57e2ad6 fix: solve more cargo test issus 2025-10-12 13:24:39 +05:30
Byson94
961f6059b7 Merge branch 'main' into iidev 2025-10-12 13:15:06 +05:30
Byson94
2f34b6271e fix(ci): Satisfying doctest 2025-10-12 13:14:56 +05:30
Byson94
2c97bce3c0 Merge branch 'main' into iidev 2025-10-12 13:08:44 +05:30
Byson94
ed8cad319d fix: fixing doctest issues with auto_plugin macro 2025-10-12 13:08:20 +05:30
Byson94
8e39fe29ba fix(ci): using arch linux docker container 2025-10-12 12:59:08 +05:30
Byson94
1ee72b03b4 feat: adding panic handling 2025-10-12 12:48:31 +05:30
Byson94
92ad2b4a0d feat: added onkeypress and onkeyrelease props 2025-10-12 12:48:31 +05:30
Byson94
61d500142c fix: fixed old widget creeping in issue 2025-10-12 12:48:31 +05:30
Byson94
cf361902c2 fix(ci): fixing packages 2025-10-12 12:48:21 +05:30
Byson94
07d6293173 chore: cargo fmt 2025-10-11 15:35:17 +05:30
Byson94
c446321173 feat: making examples in readme look good 2025-10-11 15:34:14 +05:30
Byson94
17d59c1e15 fix(ci): Fixing indentation 2025-10-11 15:08:05 +05:30
Byson94
097f4c90fd fix(ci): Working on fixing the ci not running issue 2025-10-11 15:06:01 +05:30
Byson94
87670bfe52 chore: added info in changelog 2025-10-11 14:50:25 +05:30
Byson94
f5ba140830 chore: fixing ewwii_plugin_api versioning issue 2025-10-11 14:47:30 +05:30
Byson94
854051b42f feat: added max docs and 1 feat in ewwii_plugin_api 2025-10-11 14:46:56 +05:30
Byson94
076fc443d6 fix: fixing feature issue in widget_backend.rs 2025-10-11 09:43:54 +05:30
Byson94
43a5e10d72 style: fixing ewwii_plugin_api Cargo.toml style 2025-10-11 09:41:14 +05:30
Byson94
2f27e82a7b feat: improving the ewwii_plugin_api crate and fixing issues 2025-10-11 09:38:39 +05:30
Byson94
d2871d7bc9 feat: more api changes in ewwii_plugin_api 2025-10-10 22:30:07 +05:30
Byson94
4171eafeb9 feat: improving docs and comments on ewwii_plugin_api 2025-10-10 21:43:28 +05:30
Byson94
90a1ce0958 chore: fixing ewwii_plguin_api version issue 2025-10-10 20:04:54 +05:30
Byson94
3a75c5c306 feat: added the ability to modify widgets 2025-10-10 20:04:02 +05:30
Byson94
ebc5b6409b chore: added change in changelog 2025-10-10 19:10:55 +05:30
Byson94
e8314bfced fix(rhai): fixing poll/listen scope not available on all modules 2025-10-10 19:07:37 +05:30
Byson94
06abcc0905 chore: Updating Cargo.lock 2025-10-09 22:46:24 +05:30
Byson94
168af9eb81 fix: fixing ewwii_plugin_api version 2025-10-09 22:44:13 +05:30
Byson94
f57a8862f6 feat: expanding ewwii_plugin_api 2025-10-09 22:43:38 +05:30
Byson94
e6cbb52111 feat: added tools for exporting plugins 2025-10-09 22:37:29 +05:30
Byson94
238006dbb2 feat: added option to list all widget ids in plugin sys 2025-10-09 22:02:12 +05:30
Byson94
3366f6b10c feat: added documentation for ewwii_plugin_api 2025-10-09 09:20:40 +05:30
Byson94
8706ecab72 fix(ci): fixing dependencies on gtk4 2025-10-08 21:55:03 +05:30
Byson94
2ab8e2f981 feat: updating changelog 2025-10-08 21:39:55 +05:30
Byson94
958a220163 feat: added readme and docs to plugin_api 2025-10-08 20:25:00 +05:30
Byson94
62a5b55303 feat: fixing few errors and improving other stuff 2025-10-08 19:43:44 +05:30
Byson94
4ed0cea596 chore: cargo fmt 2025-10-08 18:35:27 +05:30
Byson94
c7676f9e63 fix: fixing issue templates 2025-10-08 18:35:01 +05:30
Byson94
8cbd780479 feat: Updating changelog 2025-10-08 18:25:35 +05:30
Byson94
c999b53691 feat: getting rhai mod working via plugin 2025-10-08 18:06:34 +05:30
Byson94
af4f1a5533 feat: got library system loading working 2025-10-07 20:34:44 +05:30
Byson94
2904ba74dc wip: bin level plugin system 2025-10-07 18:56:34 +05:30
Byson94
d97a3af401 fix: fixed focusable not working 2025-10-05 17:32:50 +05:30
Byson94
c8d2d7ce87 feat: adding the release date in changelog 2025-10-04 19:54:55 +05:30
Byson94
b0e5224baa fix: fixing minor issues 2025-10-03 19:54:51 +05:30
Byson94
34546e5528 feat: removed centerbox widget 2025-10-03 18:25:04 +05:30
Byson94
7cb3a4981e GREATEST FEAT: Achived sticky windows in x11 2025-10-02 20:48:48 +05:30
Byson94
85a11e4195 fix: fixed gif images not working 2025-10-02 18:05:21 +05:30
Byson94
8d82b68ab5 fix: did a simple fix over the scale drag issue 2025-10-02 16:58:34 +05:30
Byson94
c778c98d64 chore: Cargo.lock update 2025-10-02 14:28:12 +05:30
Byson94
44cbb43472 feat: added deprecated section in changelog 2025-10-02 10:54:48 +05:30
Byson94
107ccce58a feat: added information about alpha release in changelog 2025-10-02 10:46:29 +05:30
Byson94
8fb4599a06 fix: fixing all compiler warnings 2025-10-02 10:25:52 +05:30
Byson94
db7522970f feat: fixed image wdgt and added icon wdgt 2025-10-02 10:22:24 +05:30
Byson94
0b8253b8e4 fix: no classes to window to prevent x11 issues 2025-10-02 09:37:11 +05:30
Byson94
8f38a35479 feat: fixing warnings & messin with x11 2025-10-02 08:57:04 +05:30
Byson94
5b5c805bb0 fix(x11): fixing widget not realized before getting surface 2025-10-01 20:41:59 +05:30
Byson94
6fcb411d80 fix: fixing the ewwii window close order 2025-10-01 19:09:56 +05:30
Byson94
d8ca70b5cf feat: fixing application loop issues by doing manual one 2025-10-01 18:07:18 +05:30
Byson94
7a6cdc9460 feat: fixing very single gtk4 migration error! yippee! 2025-10-01 16:53:22 +05:30
Byson94
eb0d034a46 fix: fixing one of the last few err (i think so) 2025-10-01 15:05:38 +05:30
Byson94
de3c9e7337 fix: even more fixing of gtk4 errors 2025-10-01 14:02:01 +05:30
Byson94
bb86520f43 fix: fixing the drag thingy in eventbox 2025-10-01 11:00:48 +05:30
Byson94
2e3626069e Merge branch 'main' into gtk4_migration 2025-10-01 10:10:52 +05:30
Byson94
b395d71a3c fix: fixing broken wifi gui manager screenshot 2025-10-01 10:10:39 +05:30
Byson94
f7843cb5b2 fix: fixing more errors that i cant name of 2025-10-01 10:06:35 +05:30
Byson94
9b0e042add fix: fixing many many more gtk4 migration errs 2025-10-01 10:01:10 +05:30
Byson94
5bde8145a2 fix: added controlflow in gif render part 2025-09-30 20:48:50 +05:30
Byson94
99ddaec57a fix: fixing more gtk4 related err 2025-09-30 20:45:27 +05:30
Byson94
eb46ac3d39 feat: added EventControllerKey in widget_definition.rs 2025-09-30 20:41:12 +05:30
Byson94
8aead373d1 fix: fixing gtk_layer_shell naming issues 2025-09-30 20:38:50 +05:30
Byson94
c79d79e4ce fix: fixing glib weak reference issues 2025-09-30 20:36:19 +05:30
Byson94
090dc09d39 fix: fixing timeouts in eventbox being a borrow 2025-09-30 20:03:04 +05:30
Byson94
9432deb0b9 rewrite: rewriting button and event_box widgets 2025-09-30 19:55:20 +05:30
Byson94
cd1dd82978 feat: rewriting eventbox to work with gtk4 2025-09-30 17:48:15 +05:30
Byson94
f9fafb844b fix: fixing even more errors in widget_definition 2025-09-30 13:36:30 +05:30
Byson94
bdac4492ac fix: fixing glib::clone! in widget_definitions.rust 2025-09-30 13:21:58 +05:30
Byson94
78e1f711c4 fix: fixed connect_monitor_added function 2025-09-30 13:20:30 +05:30
Byson94
9cfc88b96f fix: fixed style_provider reelated err 2025-09-30 12:29:30 +05:30
Byson94
c53ca529a8 fix: some more fix and cargo fmt 2025-09-30 12:11:15 +05:30
Byson94
b8e5bae524 fix: fixing more minor errors 2025-09-30 12:02:20 +05:30
Byson94
904e1f9dc7 feat: fixing more errors and following gtk4 model 2025-09-30 11:54:13 +05:30
Byson94
655aedc478 feat: stubbing widget creation logic for easier migration 2025-09-30 10:45:51 +05:30
Byson94
1d69805fac wip: More progress in gtk4 migration 2025-09-30 10:22:14 +05:30
Byson94
bf75ef1c36 wip: migrating to gtk4 2025-09-29 17:21:38 +05:30
Byson94
e1c9cdfcb9 feat: added name in 0.2.0 release changelog 2025-09-29 15:04:21 +05:30
Byson94
2aa5d94cea chore: a small rename :) 2025-09-29 14:29:13 +05:30
Byson94
c49d7f36f5 feat: removed monitor lib in favour of gtk4 2025-09-29 14:23:08 +05:30
Byson94
598c28bb9e feat: fixed falure send & added print flag to engine-override 2025-09-29 12:38:37 +05:30
Byson94
f56c126ef4 feat: better management of rhai crate 2025-09-29 12:11:49 +05:30
Byson94
02686bd919 feat: removed more code/dependencies 2025-09-29 12:04:37 +05:30
Byson94
37d880af2d feat: removed unnecessary dependencies 2025-09-29 11:59:07 +05:30
Byson94
04f6835892 chore: cargo fmt 2025-09-29 11:49:50 +05:30
Byson94
21e72a2e8f feat: added file path indicator in errors! 2025-09-29 11:30:53 +05:30
Byson94
497d9b664d fix: resolver throwing err at import on p/e err 2025-09-28 19:25:39 +05:30
Byson94
09b1c24b61 feat: even better err support with rhai_trace v3 2025-09-28 18:29:36 +05:30
Byson94
85a179bf81 feat: renaming setup_for_rt to setup_dyn_ids 2025-09-28 16:51:59 +05:30
Byson94
0238a9ae07 feat: removed INPUT_VAL var support in input wdgt 2025-09-28 14:45:10 +05:30
Byson94
26946b8e70 feat: improved the module heading generation 2025-09-27 19:49:00 +05:30
Byson94
97920a8bb0 fix: fixed doc error 2025-09-27 19:35:28 +05:30
Byson94
48bb71d615 chore: removed lagacy support on focusable win prop 2025-09-27 19:32:34 +05:30
Byson94
2244a1cbda feat: added force_normal property for windows 2025-09-27 19:10:30 +05:30
Byson94
0094818d73 fix: m/min duration end & ewwii crash 2025-09-27 11:49:59 +05:30
Byson94
df23beb9bb fix: fixed broken m value for min 2025-09-27 11:47:04 +05:30
Byson94
05ac8c89e5 feat: improving the engine-override feature 2025-09-26 19:26:16 +05:30
Byson94
3d5a454fe3 fix(typo): in 0.2.0 CHANGELOG 2025-09-26 19:04:09 +05:30
Byson94
a88797a92b feat: added engine-override command 2025-09-25 22:45:30 +05:30
Byson94
25ed97cc53 feat(stdlib): added std::regex for regex matching 2025-09-23 20:00:21 +05:30
Byson94
2083e0a2d1 chore: removed added support for open-many in v0.2.0 2025-09-23 19:14:04 +05:30
Byson94
b6c38aeddc feat: improved rhai docs generator 2025-09-19 17:39:07 +05:30
Byson94
b2abecdd7d feat(doc auto gen): fixed broken code for docusaurus 2025-09-19 16:50:33 +05:30
Byson94
d03ec33f70 Merge branch 'main' into iidev 2025-09-19 16:33:05 +05:30
Byson94
9ac4e57c7c feat: removing stuff in autogen 2025-09-19 16:32:55 +05:30
Byson94
c2e82d8943 feat: removing stuff in autogen 2025-09-19 16:32:40 +05:30
Byson94
85b4594ad7 Merge branch 'main' into iidev 2025-09-19 16:27:41 +05:30
Byson94
52bd01315c feat(doc gen): fixed mdx issues 2025-09-19 16:27:34 +05:30
Byson94
71f9180c5d feat(doc gen): fixed mdx issues 2025-09-19 16:27:12 +05:30
Byson94
b03f275ead Merge branch 'main' into iidev 2025-09-19 15:23:11 +05:30
Byson94
ebbd1355c6 feat: renaming js => javascript 2025-09-19 15:23:03 +05:30
Byson94
a28c5382b4 Merge branch 'main' into iidev 2025-09-19 14:16:01 +05:30
Byson94
44e7298d86 feat: removed docs/ and moving to docusarus 2025-09-19 14:15:53 +05:30
Byson94
934be31248 feat: removed docs/ and moving to docusarus 2025-09-19 14:14:50 +05:30
Byson94
767eb4e7a7 feat: added m as another duration unit for minute 2025-09-19 12:44:07 +05:30
Byson94
c10a759478 feat: more renaming | eww => ewwii 2025-09-19 12:39:49 +05:30
Byson94
e9cc5f1518 feat: removing a few comment of minee 2025-09-19 12:37:54 +05:30
Byson94
ee37ea5555 feat: fully renamed EwwPaths => EwwiiPaths 2025-09-19 12:34:02 +05:30
Byson94
8800b9b887 feat: made eval_code fn use the compile_code fn to compile 2025-09-19 12:16:39 +05:30
Byson94
b9368d912c chore: renaming a few eww => ewwii 2025-09-19 11:58:46 +05:30
Byson94
a75cfd65f3 feat: added state command and fixed log issues 2025-09-19 11:55:44 +05:30
Byson94
bd7f1f58bf feat: added --preserve in the docs 2025-09-18 20:42:03 +05:30
Byson94
ad7229039f feat: added date in 0.1.4 release 2025-09-18 20:36:04 +05:30
Byson94
9ddfad0fbe feat: added perserve flag to the update command which preserves the new updates 2025-09-18 20:17:44 +05:30
Byson94
10a7f8a1da chore(cargo): building for 0.1.3 2025-09-17 22:15:38 +05:30
Byson94
0a1f981bb1 feat: fixed image_width and height not working issue for image 2025-09-17 22:12:39 +05:30
Byson94
9053cf8700 feat: removed getting started section from docs 2025-09-14 10:54:17 +05:30
Byson94
909b33a897 chore: fixing a comment... 2025-09-14 09:46:49 +05:30
Byson94
b96a34a1f4 feat: reworking the inject-vars arg of update 2025-09-14 09:44:44 +05:30
Byson94
11e21bbdda feat: made update cmd so that it will preserve current wdgt state 2025-09-13 14:40:54 +05:30
Byson94
e18b6da679 feat(docs): added exclusive true in config 2025-09-13 13:28:59 +05:30
Byson94
dc135f22a9 fix(docs): fixed invalid config in configuration.md 2025-09-13 13:27:26 +05:30
Byson94
fc88f6e38d feat: updating ewwii dark theme 2025-09-13 13:20:15 +05:30
Byson94
fa5be3a73a feat: improved parent-death sig on freebsd and linux 2025-09-13 10:01:23 +05:30
Byson94
26c20d34a3 feat: removed todo 2025-09-10 18:09:26 +05:30
115 changed files with 5172 additions and 12627 deletions

View File

@@ -7,9 +7,9 @@ body:
attributes:
label: Checklist before submitting an issue
options:
- label: I have searched through the existing [closed and open issues](https://github.com/elkowar/eww/issues?q=is%3Aissue) for eww and made sure this is not a duplicate
- label: I have searched through the existing [closed and open issues](https://github.com/Ewwii-sh/ewwii/issues?q=is%3Aissue) for ewwii and made sure this is not a duplicate
required: true
- label: I have specifically verified that this bug is not a common [user error](https://github.com/elkowar/eww/issues?q=is%3Aissue+label%3Ano-actual-bug+is%3Aclosed)
- label: I have specifically verified that this bug is not a common [user error](https://github.com/Ewwii-sh/ewwii/issues?q=is%3Aissue+label%3Ano-actual-bug+is%3Aclosed)
required: true
- label: I am providing as much relevant information as I am able to in this bug report (Minimal config to reproduce the issue for example, if applicable)
required: true
@@ -40,6 +40,6 @@ body:
- type: textarea
attributes:
label: "Platform and environment"
description: "Does this happen on wayland, X11, or on both? What WM/Compositor are you using? Which version of eww are you using? (when using a git version, optimally provide the exact commit ref)."
description: "Does this happen on wayland, X11, or on both? What WM/Compositor are you using? Which version of ewwii are you using? (when using a git version, optimally provide the exact commit ref)."
validations:
required: true

View File

@@ -12,7 +12,7 @@ body:
- type: textarea
attributes:
label: "Proposed configuration syntax"
description: "If the feature you are requesting would add or change something to the Eww configuration, please provide an example for how the feature could be used."
description: "If the feature you are requesting would add or change something to the Ewwii configuration, please provide an example for how the feature could be used."
validations:
required: false
- type: textarea

View File

@@ -26,5 +26,4 @@ Please make sure you can check all the boxes that apply to this PR.
- [ ] All widgets I've added are correctly documented.
- [ ] I added my changes to CHANGELOG.md, if appropriate.
- [ ] The documentation in the `docs/content/main` directory has been adjusted to reflect my changes.
- [ ] I used `cargo fmt` to automatically format all code before committing

View File

@@ -1,45 +1,56 @@
name: build
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
push:
branches:
- main
pull_request:
branches:
- main
env:
CARGO_TERM_COLOR: always
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install libgtk-3-dev libgtk-layer-shell-dev libdbusmenu-gtk3-dev
build:
runs-on: ubuntu-latest
container:
image: archlinux:latest
- uses: actions/checkout@v4
steps:
- name: Install dependencies
run: |
pacman -Syu --noconfirm
pacman -S --noconfirm base-devel gtk4 gtk4-layer-shell pkgconf git
- name: Setup rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy,rustfmt
- uses: actions/checkout@v4
- name: Load rust cache
uses: Swatinem/rust-cache@v2
- name: Setup rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy,rustfmt
- name: Setup problem matchers
uses: r7kamura/rust-problem-matchers@v1
- name: Load rust cache
uses: Swatinem/rust-cache@v2
- name: Check formatting
run: cargo fmt -- --check
- name: Check with default features
run: cargo check
- name: Setup problem matchers
uses: r7kamura/rust-problem-matchers@v1
- name: Run tests
run: cargo test
- name: Check formatting
run: cargo fmt -- --check
- name: Check x11 only
run: cargo check --no-default-features --features=x11
- name: Check wayland only
run: cargo check --no-default-features --features=wayland
- name: Check no-backend
run: cargo check --no-default-features
- name: Check with default features
run: cargo check
- name: Run tests
run: cargo test
- name: Check x11 only
run: cargo check --no-default-features --features=x11
- name: Check wayland only
run: cargo check --no-default-features --features=wayland
- name: Check no-backend
run: cargo check --no-default-features

View File

@@ -1,93 +0,0 @@
name: Build and deploy Github pages
on:
push:
branches:
- main
paths:
- "docs/**"
- ".github/workflows/**"
- "tools/generate-rhai-docs/**"
permissions:
contents: write
jobs:
build:
name: Build mdBook
runs-on: ubuntu-latest
steps:
# Checkout code
- name: Checkout repository
uses: actions/checkout@v4
# Cache apt package lists
- name: Cache apt-get package lists
uses: actions/cache@v3
with:
path: |
/var/cache/apt/archives
/var/lib/apt/lists
key: ${{ runner.os }}-apt-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-apt-
# Install necessary dependencies for glib-sys and gobject-sys
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
pkg-config \
libcairo2-dev \
libglib2.0-dev \
libgirepository1.0-dev \
libpango1.0-dev \
libatk1.0-dev \
libgtk-3-dev
# Cache Rust toolchain
- name: Cache Rust toolchain
uses: actions/cache@v4
with:
path: ~/.cargo
key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-rust-
# Install Rust
- name: Install Rust
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
# Cache Cargo registry and git directory
- name: Cache Cargo registry and git
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
# Generate module docs
- name: Generate module docs
run: cargo run --release -p generate-rhai-docs
# Build mdBook
- name: Build mdBook page
uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: "0.4.52"
- name: Run mdBook build
run: mdbook build docs
# Deploy to GitHub Pages
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/book/

View File

@@ -5,6 +5,173 @@ All notable changes to `ewwii` are documented here.
This changelog follows the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format,
and this project adheres to [Semantic Versioning](https://semver.org/).
## [UNRELEASED]
### Added
- `gtk_ui` function for loading .ui files.
- `widget-control` (`wc` in short) command for controlling widgets.
- `placeholder` property to input widget.
- `transition_duration` property to stack widget.
- `widget_control` utility function for dynamic widget handling.
- `text` and `show_text` property to progressbar widget.
- `content_fit` property to image widget.
- `can_shrink` property to image widget.
- `mutations` property to localsignal.
- `eval_ignore` property to all widgets.
- Touch support to scale widget.
### Fixed
- `clockwise` property not working on circular_progress.
### Removed
- icon widget.
## [0.3.1] - 2025-11-01
## Fixed
- Circular progress bar not updating dynamically.
- LocalSignal values not getting transformed to suite property type.
- LocalBind not finding properties of range subclasses.
## [0.3.0] - 2025-11-01
### Added
- `localsignal` signal for fast and cheap property update.
- `localbind` utility for binding `localsignal` to a widget property.
- `onkeypress` property to eventbox.
- `onkeyrelease` property to eventbox.
- Selection of dash as shell if it installed.
- `--with-plugin` flag for `daemon` command.
- `register_function` API in ewwii_plugin_api for registering functions that rhai can call to.
- `slib` rhai module for calling functions registered via `register_function`.
- `orientation` property to eventbox.
- `spacing` property to eventbox.
- `space_evenly` property to eventbox.
- An advanced widget named `flowbox`.
- `focusable` property to all widget.
- `widget_name` property to all widget.
- `lifetime` flag for update command.
- `circular-progress` widget back.
### Fixed
- Old widget creeping into new ones after hot reload.
- Ewwii defaulting to default gtk4 theme. #9
- Few issues with eventbox drop target.
- Poll/Listen variables not exposed in other modules on first launch.
- Not able to define poll/listen variables in other files.
- Overlay and Tooltip widgets not being reactive.
## [0.3.0-beta] - 2025-10-11
### Added
- Support for binary level plugins.
- `set-plugin` command to load shared libraries (i.e plugins).
- `ewwii_plugin_api` crate for building ewwii plugins easily.
### Changed
- The name of `slider` widget to `scale`.
### Fixed
- `focusable` property not working issue.
- Poll/Listen variable scope not accessable to all modules.
## [0.3.0-alpha] - 2025-10-04
### Added
- GTK4 support.
- `can_target` boolean property for all widgets.
- `icon` widget which always preserves a scaling of 1:1.
- Full native wayland compatibility (x11 compatibility has decreased).
### Changed
- The way dynamic updates are handled.
- Window positioning logic.
- X11 communication and x11 based window handling.
- Daemon GTK main loop.
- Application of sticky and stacking propery of window on X11.
- The full implementation of eventbox widget.
- GTK controller/signal handling.
- Initialization logic of window.
- Overlay widget dynamic post-creation re-creation logic (bugs are expected).
### Removed
- `show_details` property of calendar.
- `angle` property from label.
- `icon_size` property from image widget.
- `same_size` property from stack widget (box is a replacement).
- `icon_name` from `image` widget as a result of the introduction of `icon` widget.
- `transform`, `graph`, and `circular_progress` widget (temporarily).
- Application of css class on the window.
- Legacy GTK3 related code.
- Centerbox widget.
## [0.2.0] - 2025-09-29
### Added
- `state` command to print the current poll/listen variable state.
- `m` as another duration unit for minute.
- `std::regex` library for regex matching.
- `engine-override` command which can be used to change engine settings of a configuration.
- `force_normal` property for ewwii windows. It allows user to create normal windows on wayland.
- Better error support by migrating to `rhai_trace` v0.3.0.
- File path indicator in rhai errors.
### Fixed
- The logs going to `eww_{}.log` instead of `ewwii_{}.log`.
- Logs not truncating if it is over 100MB and not deleting if over 7 days old.
- Ewwii crashing on invalid duration property.
- The module resolver throwing error at `import` defenition.
- Fixed commands sending error with success status.
### Removed
- Legacy `true`/`false` support for `focusable` window property.
- `$INPUT_VAL` variable injected in commands ran by input widget.
- Many dependencies and code for faster build and lesser binary size.
- `monitor` library as a step towards GTK4.
## [0.1.4] - 2025-09-18
### Added
- `--preserve` flag to the `update` command which preserves the new updates.
## [0.1.3] - 2025-09-17
### Changed
- `update` command so that it preserves current widget state.
- `--inject-vars` argument of update to just `--inject` (or `-i` in short).
### Fixed
- `image_width` and `image_height` not working for image widget.
## [0.1.2] - 2025-09-13
### Added
- "Parent-death signal is not supported" warning on macOS.
- Error logging on parent-death signal fail.
### Fixed
- Code not compiling for FreeBSD.
## [0.1.1] - 2025-09-07
### Added

796
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,18 @@
[workspace]
members = ["crates/*", "tools/*"]
members = ["crates/*", "tools/*", "proc_macros/*"]
resolver = "2"
[workspace.dependencies]
shared_utils = { version = "0.1.0", path = "crates/shared_utils" }
rhai_impl = { version = "0.1.0", path = "crates/rhai_impl" }
scan_prop_proc = { version = "0.1.0", path = "proc_macros/scan_prop_proc" }
ewwii_plugin_api = { version = "0.7.0", path = "crates/ewwii_plugin_api" }
anyhow = "1.0.86"
ahash = "0.8.12"
bincode = "1.3.3"
bytesize = "2.0.1"
cached = "0.53.1"
chrono = "0.4.38"
chrono-tz = "0.10.0"
clap = { version = "4.5.1", features = ["derive"] }
clap_complete = "4.5.12"
codespan-reporting = "0.11"
@@ -27,14 +26,8 @@ derive_more = { version = "1", features = [
extend = "1.2"
futures = "0.3.30"
grass = "0.13.4"
gtk = "0.18.1"
insta = "1.7"
gtk4 = { version = "0.10.1", features = ["v4_8"] }
itertools = "0.13.0"
jaq-core = "1.5.1"
jaq-parse = "1.0.3"
jaq-std = "1.6.0"
jaq-interpret = "1.5.0"
jaq-syn = "1.6.0"
libc = "0.2"
log = "0.4"
nix = "0.29.0"
@@ -42,20 +35,21 @@ notify = "6.1.1"
once_cell = "1.19"
pretty_assertions = "1.4.0"
pretty_env_logger = "0.5.0"
ref-cast = "1.0.22"
regex = "1.10.5"
rhai = { version = "1.22.2", features = ["internals"] }
rhai = "1.23.6"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
simple-signal = "1.1"
smart-default = "0.7.1"
static_assertions = "1.1.0"
strsim = "0.11"
strum = { version = "0.26", features = ["derive"] }
thiserror = "1.0"
tokio = { version = "1.39.2", features = ["full"] }
unescape = "0.1"
wait-timeout = "0.2"
syn = "2.0.107"
quote = "1.0.41"
proc-macro2 = "1.0.101"
shell-words = "1.1.0"
[profile.dev]
split-debuginfo = "unpacked"

View File

@@ -1,25 +1,31 @@
[![dependency status](https://deps.rs/repo/github/byson94/ewwii/status.svg)](https://deps.rs/repo/github/byson94/ewwii)
[![docs link](https://img.shields.io/badge/documentation-link-blue)](https://ewwii-sh.github.io/docs)
# Ewwii
<img src="./.github/EwwiiLogo.png" height="100" align="left"/>
Elkowars Wacky Widgets Imporved Interface is a fork of Elkowars Wacky Widgets which is a standalone widget system made in Rust that allows you to implement your own, custom widgets in any window manager.
Elkowars Wacky Widgets Improved Interface is a fork of Elkowars Wacky Widgets which is a standalone widget system made in Rust that allows you to implement your own, custom widgets in any window manager.
## Examples
All examples are in the [examples directory](./examples/).
Examples of projects powered by ewwii.
- A basic bar [CLICK TO SEE](./examples/ewwii-bar) <br>
![Example 1](./examples/ewwii-bar/ewwii-bar.png)
| Project | Preview |
|---------|---------|
| **Basic Bar**<br>[- View Example](./examples/ewwii-bar) | [![Basic Bar](./examples/ewwii-bar/ewwii-bar.png)](./examples/ewwii-bar) |
| **Data Structures**<br>[- View Example](./examples/data-structures) | [![Data Structures](./examples/data-structures/data-structures-preview.png)](./examples/data-structures) |
| **Wi-Fi GUI Template**<br>[- View on GitHub](https://github.com/Ewwii-sh/ewifi_gui_template) | ![Wi-Fi GUI Template](https://raw.githubusercontent.com/Ewwii-sh/ewifi_gui_template/main/.github/wifi_manager_template.png) |
| **Obsidian Bar Template**<br>[- View on GitHub](https://github.com/Ewwii-sh/obsidian-bar) | [![Obsidian Bar](https://raw.githubusercontent.com/Ewwii-sh/obsidian-bar/main/.github/screenshot.png)](https://github.com/Ewwii-sh/obsidian-bar) |
| **Binary Dots by [@BinaryHarbinger](https://github.com/BinaryHarbinger)**<br>[- View on GitHub](https://github.com/BinaryHarbinger/binarydots/) | [![Binary Dots](https://raw.githubusercontent.com/BinaryHarbinger/binarydots/main/preview/Desktop.png)](https://github.com/BinaryHarbinger/binarydots)
| **Astatine Dots (Linux Rice with Ewwii)**<br>[- View on GitHub](https://github.com/Ewwii-sh/astatine-dots) | [![Astatine Dots](https://github.com/user-attachments/assets/f028ca1f-e403-476d-a7d9-cadce47691b7)](https://github.com/Ewwii-sh/astatine-dots) |
- Data structures [CLICK TO SEE](./examples/data-structures) <br>
![Example 2](./examples/data-structures/data-structures-preview.png)
## Features
## Templates
- A wifi gui template [CLICK TO SEE](https://github.com/Ewwii-sh/ewifi_gui_template) <br>
<img src="https://raw.githubusercontent.com/Ewwii-sh/ewwii/main/docs/src/images/wifi_manager_template.png" width="400" />
- Powered by Gtk4
- Supports Hot reload
- Extensibility via plugins and rhai modules
- X11 + Wayland support
## Contribewwtiing

View File

@@ -1,2 +0,0 @@
- Migrate to GTK4
- Fix state not updating on startup bug

View File

@@ -1,6 +1,6 @@
[package]
name = "ewwii"
version = "0.1.0"
version = "0.4.0"
authors = ["byson94 <byson94wastaken@gmail.com>"]
description = "Widgets for everyone made better!"
license = "GPL-3.0-or-later"
@@ -11,17 +11,17 @@ edition = "2021"
[features]
default = ["x11", "wayland"]
x11 = ["gdkx11", "x11rb"]
wayland = ["gtk-layer-shell"]
x11 = ["gdk4-x11", "x11rb"]
wayland = ["gtk4-layer-shell"]
[dependencies]
shared_utils.workspace = true
rhai_impl.workspace = true
ewwii_plugin_api.workspace = true
gtk-layer-shell = { version = "0.8.1", optional = true, features=["v0_6"] }
gdkx11 = { version = "0.18", optional = true }
gtk4-layer-shell = { version = "0.6.3", optional = true }
gdk4-x11 = { version = "0.10.1", optional = true }
x11rb = { version = "0.13.1", features = ["randr"], optional = true }
gdk-sys = "0.18.0"
grass.workspace = true
thiserror.workspace = true
@@ -35,7 +35,7 @@ derive_more.workspace = true
extend.workspace = true
futures.workspace = true
smart-default.workspace = true
gtk.workspace = true
gtk4.workspace = true
itertools.workspace = true
log.workspace = true
nix = { workspace = true, features = ["process", "fs", "signal"] }
@@ -49,7 +49,10 @@ simple-signal.workspace = true
tokio = { workspace = true, features = ["full"] }
unescape.workspace = true
wait-timeout.workspace = true
rhai.workspace = true
rhai = { workspace = true, features = ["internals"] }
shell-words.workspace = true
# Plugin loading
libloading = "0.8.9"
[dev-dependencies]
pretty_assertions.workspace = true

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ use std::process::Stdio;
use crate::{
daemon_response::DaemonResponse,
opts::{self, ActionClientOnly},
paths::EwwPaths,
paths::EwwiiPaths,
};
use anyhow::{Context, Result};
use std::{
@@ -11,7 +11,7 @@ use std::{
os::unix::net::UnixStream,
};
pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) -> Result<()> {
pub fn handle_client_only_action(paths: &EwwiiPaths, action: ActionClientOnly) -> Result<()> {
match action {
ActionClientOnly::Logs => {
std::process::Command::new("tail")

View File

@@ -1,10 +1,11 @@
use crate::{
// ipc_server,
// error_handling_ctx,
paths::EwwPaths,
paths::EwwiiPaths,
window::backend_window_options::BackendWindowOptionsDef,
};
use anyhow::{bail, Context, Result};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
@@ -13,10 +14,13 @@ use rhai_impl::{ast::WidgetNode, parser::ParseConfig};
// use tokio::{net::UnixStream, runtime::Runtime, sync::mpsc};
/// Load an [`EwwiiConfig`] from the config dir of the given [`crate::EwwPaths`],
/// Load an [`EwwiiConfig`] from the config dir of the given [`crate::EwwiiPaths`],
/// resetting and applying the global YuckFiles object in [`crate::error_handling_ctx`].
pub fn read_from_ewwii_paths(eww_paths: &EwwPaths) -> Result<EwwiiConfig> {
EwwiiConfig::read_from_dir(eww_paths)
pub fn read_from_ewwii_paths(
eww_paths: &EwwiiPaths,
parser: &mut ParseConfig,
) -> Result<EwwiiConfig> {
EwwiiConfig::read_from_dir(eww_paths, parser)
}
/// Ewwii configuration structure.
@@ -24,7 +28,7 @@ pub fn read_from_ewwii_paths(eww_paths: &EwwPaths) -> Result<EwwiiConfig> {
pub struct EwwiiConfig {
windows: HashMap<String, WindowDefinition>,
root_node: Option<Rc<WidgetNode>>,
compiled_ast: Option<Rc<AST>>,
compiled_ast: Option<Rc<RefCell<AST>>>,
}
#[derive(Debug, Clone)]
@@ -36,26 +40,28 @@ pub struct WindowDefinition {
}
impl EwwiiConfig {
/// Load an [`EwwiiConfig`] from the config dir of the given [`crate::EwwPaths`], reading the main config file.
pub fn read_from_dir(eww_paths: &EwwPaths) -> Result<Self> {
/// Load an [`EwwiiConfig`] from the config dir of the given [`crate::EwwiiPaths`], reading the main config file.
pub fn read_from_dir(eww_paths: &EwwiiPaths, config_parser: &mut ParseConfig) -> Result<Self> {
let rhai_path = eww_paths.get_rhai_path();
if !rhai_path.exists() {
bail!("The configuration file `{}` does not exist", rhai_path.display());
}
// initialize configuration parser
let mut config_parser = ParseConfig::new();
// get code from file
let rhai_code = config_parser.code_from_file(&rhai_path)?;
// Get Option<&str> form of rhai_path
let rhai_path_opt_str = rhai_path.to_str();
// get the iirhai widget tree
let compiled_ast = config_parser.compile_code(&rhai_code)?;
let compiled_ast =
config_parser.compile_code(&rhai_code, rhai_path_opt_str.unwrap_or("<rhai>"))?;
let poll_listen_scope = ParseConfig::initial_poll_listen_scope(&rhai_code)?;
let config_tree = config_parser.eval_code_with(
&rhai_code,
Some(poll_listen_scope),
Some(&compiled_ast),
rhai_path_opt_str,
)?;
let mut window_definitions = HashMap::new();
@@ -82,7 +88,7 @@ impl EwwiiConfig {
Ok(EwwiiConfig {
windows: window_definitions,
root_node: Some(Rc::new(config_tree)),
compiled_ast: Some(Rc::new(compiled_ast)),
compiled_ast: Some(Rc::new(RefCell::new(compiled_ast))),
})
}
@@ -104,20 +110,18 @@ impl EwwiiConfig {
self.root_node.clone().ok_or_else(|| anyhow::anyhow!("root_node is missing"))
}
pub fn get_windows_root_widget(config_tree: WidgetNode) -> Result<WidgetNode> {
if let WidgetNode::Enter(children) = config_tree {
for node in children {
if let WidgetNode::DefWindow { node: boxed_node, .. } = node {
return Ok(*boxed_node);
}
}
bail!("No `DefWindow` found inside `Enter`");
} else {
bail!("Expected root node to be `Enter`, but got something else.");
}
}
pub fn get_owned_compiled_ast(&self) -> Option<Rc<AST>> {
pub fn get_owned_compiled_ast(&self) -> Option<Rc<RefCell<AST>>> {
self.compiled_ast.clone()
}
pub fn replace_data(&mut self, new_dat: Self) {
if let (Some(old_ast_rc), Some(new_ast_rc)) =
(self.compiled_ast.as_ref(), new_dat.compiled_ast.as_ref())
{
*old_ast_rc.borrow_mut() = new_ast_rc.borrow().clone();
}
self.windows = new_dat.windows;
self.root_node = new_dat.root_node;
}
}

View File

@@ -1,6 +1,6 @@
//! Types to manage messages that notify the eww client over the result of a command
//! Types to manage messages that notify the ewwii client over the result of a command
//!
//! Communcation between the daemon and eww client happens via IPC.
//! Communcation between the daemon and ewwii client happens via IPC.
//! If the daemon needs to send messages back to the client as a response to a command (mostly for CLI output),
//! this happens via the DaemonResponse types

View File

@@ -1,6 +1,7 @@
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
use crate::window_initiator::WindowInitiator;
use gtk4::Window;
use gtk::gdk;
use gtk4::gdk;
#[cfg(feature = "wayland")]
pub use platform_wayland::WaylandBackend;
@@ -32,10 +33,15 @@ impl DisplayBackend for NoBackend {
fn initialize_window(
_window_init: &WindowInitiator,
_monitor: gdk::Rectangle,
x: i32,
y: i32,
_x: i32,
_y: i32,
) -> Option<Window> {
Some(Window::new(gtk::WindowType::Toplevel, x, y))
// top level
let window = Window::new();
// window.move_(x, y);
Some(window)
}
}
@@ -45,10 +51,11 @@ mod platform_wayland {
use crate::window::backend_window_options::WlWindowFocusable;
use crate::window::window_definition::WindowStacking;
use crate::window::window_geometry::AnchorAlignment;
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
use gtk::gdk;
use gtk::prelude::*;
use gtk_layer_shell::{KeyboardMode, LayerShell};
use crate::window_initiator::WindowInitiator;
use gtk4::gdk;
use gtk4::prelude::*;
use gtk4::Window;
use gtk4_layer_shell::{KeyboardMode, LayerShell};
pub struct WaylandBackend;
@@ -59,33 +66,88 @@ mod platform_wayland {
fn initialize_window(
window_init: &WindowInitiator,
monitor: gdk::Rectangle,
x: i32,
y: i32,
_x: i32,
_y: i32,
) -> Option<Window> {
let window = Window::new(gtk::WindowType::Toplevel, x, y);
// Initialising a layer shell surface
window.init_layer_shell();
// Sets the monitor where the surface is shown
if let Some(ident) = window_init.monitor.clone() {
let display = gdk::Display::default().expect("could not get default display");
if let Some(monitor) = crate::app::get_monitor_from_display(&display, &ident) {
window.set_monitor(&monitor);
} else {
return None;
}
};
let window = Window::new();
// window.move_(x, y);
window.set_resizable(window_init.resizable);
// Sets the layer where the layer shell surface will spawn
match window_init.stacking {
WindowStacking::Foreground => window.set_layer(gtk_layer_shell::Layer::Top),
WindowStacking::Background => window.set_layer(gtk_layer_shell::Layer::Background),
WindowStacking::Bottom => window.set_layer(gtk_layer_shell::Layer::Bottom),
WindowStacking::Overlay => window.set_layer(gtk_layer_shell::Layer::Overlay),
}
if !window_init.backend_options.wayland.force_normal {
// Initialising a layer shell surface
window.init_layer_shell();
// Sets the monitor where the surface is shown
if let Some(ident) = window_init.monitor.clone() {
let display = gdk::Display::default().expect("could not get default display");
if let Some(monitor) = crate::app::get_monitor_from_display(&display, &ident) {
window.set_monitor(Some(&monitor));
} else {
return None;
}
};
if let Some(namespace) = &window_init.backend_options.wayland.namespace {
window.set_namespace(namespace);
// Sets the layer where the layer shell surface will spawn
match window_init.stacking {
WindowStacking::Foreground => window.set_layer(gtk4_layer_shell::Layer::Top),
WindowStacking::Background => {
window.set_layer(gtk4_layer_shell::Layer::Background)
}
WindowStacking::Bottom => window.set_layer(gtk4_layer_shell::Layer::Bottom),
WindowStacking::Overlay => window.set_layer(gtk4_layer_shell::Layer::Overlay),
}
if let Some(namespace) = &window_init.backend_options.wayland.namespace {
window.set_namespace(Some(namespace));
}
if let Some(geometry) = window_init.geometry {
// Positioning surface
let mut top = false;
let mut left = false;
let mut right = false;
let mut bottom = false;
match geometry.anchor_point.x {
AnchorAlignment::START => left = true,
AnchorAlignment::CENTER => {}
AnchorAlignment::END => right = true,
}
match geometry.anchor_point.y {
AnchorAlignment::START => top = true,
AnchorAlignment::CENTER => {}
AnchorAlignment::END => bottom = true,
}
window.set_anchor(gtk4_layer_shell::Edge::Left, left);
window.set_anchor(gtk4_layer_shell::Edge::Right, right);
window.set_anchor(gtk4_layer_shell::Edge::Top, top);
window.set_anchor(gtk4_layer_shell::Edge::Bottom, bottom);
let xoffset = geometry.offset.x.pixels_relative_to(monitor.width());
let yoffset = geometry.offset.y.pixels_relative_to(monitor.height());
if left {
window.set_margin(gtk4_layer_shell::Edge::Left, xoffset);
} else {
window.set_margin(gtk4_layer_shell::Edge::Right, xoffset);
}
if bottom {
window.set_margin(gtk4_layer_shell::Edge::Bottom, yoffset);
} else {
window.set_margin(gtk4_layer_shell::Edge::Top, yoffset);
}
// https://github.com/elkowar/eww/issues/296
if window_init.backend_options.wayland.exclusive
&& geometry.anchor_point.x != AnchorAlignment::CENTER
&& geometry.anchor_point.y != AnchorAlignment::CENTER
{
log::warn!("When ':exclusive true' the anchor has to include 'center', otherwise exlcusive won't work")
}
}
if window_init.backend_options.wayland.exclusive {
window.auto_exclusive_zone_enable();
}
}
// Sets the keyboard interactivity
@@ -95,53 +157,6 @@ mod platform_wayland {
WlWindowFocusable::OnDemand => window.set_keyboard_mode(KeyboardMode::OnDemand),
}
if let Some(geometry) = window_init.geometry {
// Positioning surface
let mut top = false;
let mut left = false;
let mut right = false;
let mut bottom = false;
match geometry.anchor_point.x {
AnchorAlignment::START => left = true,
AnchorAlignment::CENTER => {}
AnchorAlignment::END => right = true,
}
match geometry.anchor_point.y {
AnchorAlignment::START => top = true,
AnchorAlignment::CENTER => {}
AnchorAlignment::END => bottom = true,
}
window.set_anchor(gtk_layer_shell::Edge::Left, left);
window.set_anchor(gtk_layer_shell::Edge::Right, right);
window.set_anchor(gtk_layer_shell::Edge::Top, top);
window.set_anchor(gtk_layer_shell::Edge::Bottom, bottom);
let xoffset = geometry.offset.x.pixels_relative_to(monitor.width());
let yoffset = geometry.offset.y.pixels_relative_to(monitor.height());
if left {
window.set_layer_shell_margin(gtk_layer_shell::Edge::Left, xoffset);
} else {
window.set_layer_shell_margin(gtk_layer_shell::Edge::Right, xoffset);
}
if bottom {
window.set_layer_shell_margin(gtk_layer_shell::Edge::Bottom, yoffset);
} else {
window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, yoffset);
}
// https://github.com/elkowar/eww/issues/296
if window_init.backend_options.wayland.exclusive
&& geometry.anchor_point.x != AnchorAlignment::CENTER
&& geometry.anchor_point.y != AnchorAlignment::CENTER
{
log::warn!("When ':exclusive true' the anchor has to include 'center', otherwise exlcusive won't work")
}
}
if window_init.backend_options.wayland.exclusive {
window.auto_exclusive_zone_enable();
}
Some(window)
}
}
@@ -152,11 +167,13 @@ mod platform_x11 {
use crate::window::backend_window_options::Side;
use crate::window::backend_window_options::X11WindowType;
use crate::window::window_definition::WindowStacking;
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
use crate::window_initiator::WindowInitiator;
use anyhow::{Context, Result};
use gdk::Monitor;
use gtk::gdk;
use gtk::{self, prelude::*};
use gtk4::gdk;
use gtk4::Window;
use gtk4::{self, prelude::*};
use std::rc::Rc;
use x11rb::protocol::xproto::ConnectionExt;
use x11rb::{
@@ -176,23 +193,19 @@ mod platform_x11 {
fn initialize_window(
window_init: &WindowInitiator,
_monitor: gdk::Rectangle,
x: i32,
y: i32,
_x: i32,
_y: i32,
) -> Option<Window> {
let window_type = if window_init.backend_options.x11.wm_ignore {
gtk::WindowType::Popup
} else {
gtk::WindowType::Toplevel
};
let window = Window::new(window_type, x, y);
let window = Window::new();
if window_init.backend_options.x11.wm_ignore {
// popup
window.set_decorated(false);
window.set_modal(true);
}; // else: normal, toplevel
// window.move_(x, y);
window.set_resizable(window_init.resizable);
window.set_keep_above(window_init.stacking == WindowStacking::Foreground);
window.set_keep_below(window_init.stacking == WindowStacking::Background);
if window_init.backend_options.x11.sticky {
window.stick();
} else {
window.unstick();
}
Some(window)
}
}
@@ -208,9 +221,9 @@ mod platform_x11 {
}
struct X11BackendConnection {
conn: RustConnection<DefaultStream>,
conn: Rc<RustConnection<DefaultStream>>,
root_window: u32,
atoms: AtomCollection,
atoms: Rc<AtomCollection>,
}
impl X11BackendConnection {
@@ -218,7 +231,11 @@ mod platform_x11 {
let (conn, screen_num) = RustConnection::connect(None)?;
let screen = conn.setup().roots[screen_num].clone();
let atoms = AtomCollection::new(&conn)?.reply()?;
Ok(X11BackendConnection { conn, root_window: screen.root, atoms })
Ok(X11BackendConnection {
conn: Rc::new(conn),
root_window: screen.root,
atoms: Rc::new(atoms),
})
}
fn set_xprops_for(
@@ -229,11 +246,13 @@ mod platform_x11 {
) -> Result<()> {
let monitor_rect = monitor.geometry();
let scale_factor = monitor.scale_factor() as u32;
let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?;
let win_id = gdk_window
.downcast_ref::<gdkx11::X11Window>()
let gdk_surface =
window.surface().context("Couldn't get gdk window from gtk window")?;
let win_id = gdk_surface
.downcast_ref::<gdk4_x11::X11Surface>()
.context("Failed to get x11 window for gtk window")?
.xid() as u32;
let strut_def = window_init.backend_options.x11.struts;
let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?;
@@ -306,8 +325,63 @@ mod platform_x11 {
)?
.check()?;
// apply the stickiness and fg/bg thingy
let sticky_clone = window_init.backend_options.x11.sticky.clone();
let stacking_clone = window_init.stacking.clone();
let root_window = self.root_window;
let conn = Rc::clone(&self.conn);
let atoms = Rc::clone(&self.atoms);
window.connect_show(move |_| {
if let Err(err) = Self::set_window_states(
&*conn,
win_id,
&*atoms,
sticky_clone,
stacking_clone,
root_window,
) {
log::error!("Failed to set window state: {}", err);
}
});
self.conn.flush().context("Failed to send requests to X server")
}
fn set_window_states(
conn: &impl Connection,
win: u32,
atoms: &AtomCollection,
sticky: bool,
stacking: WindowStacking,
root_window: u32,
) -> Result<()> {
let mut states = Vec::new();
if sticky {
states.push(atoms._NET_WM_STATE_STICKY);
}
match stacking {
WindowStacking::Foreground => states.push(atoms._NET_WM_STATE_ABOVE),
WindowStacking::Background => states.push(atoms._NET_WM_STATE_BELOW),
_ => {}
}
for state in states {
let event = ClientMessageEvent {
response_type: CLIENT_MESSAGE_EVENT,
format: 32,
sequence: 0,
window: win,
type_: atoms._NET_WM_STATE,
data: ClientMessageData::from([1, state, 0, 0, 0]),
};
conn.send_event(true, root_window, EventMask::PROPERTY_CHANGE, event)?;
}
conn.flush()?;
Ok(())
}
}
x11rb::atom_manager! {

View File

@@ -18,9 +18,9 @@
#![allow(rustdoc::private_intra_doc_links)]
// getting gtk stuff
extern crate gtk;
extern crate gtk4;
#[cfg(feature = "wayland")]
extern crate gtk_layer_shell as gtk_layer_shell;
extern crate gtk4_layer_shell as gtk4_layer_shell;
// imporing dependencies
use anyhow::{Context, Result};
@@ -28,7 +28,7 @@ use clap::CommandFactory as _;
use daemon_response::{DaemonResponse, DaemonResponseReceiver};
use display_backend::DisplayBackend;
use opts::ActionWithServer;
use paths::EwwPaths;
use paths::EwwiiPaths;
use std::{os::unix::net, path::Path, time::Duration};
use crate::server::ForkResult;
@@ -48,6 +48,7 @@ mod gen_diagnostic_macro;
mod ipc_server;
mod opts;
mod paths;
mod plugin;
mod server;
mod util;
mod widgets;
@@ -56,9 +57,8 @@ mod window_arguments;
mod window_initiator;
fn main() {
// gets the eww binary
let eww_binary_name = std::env::args().next().unwrap();
let opts: opts::Opt = opts::Opt::from_env(); // opts of clap (from ./opts.rs)
let ewwii_binary_name = std::env::args().next().unwrap();
let opts: opts::Opt = opts::Opt::from_env();
let trace_enabled = std::env::var("EWWII_TRACE").is_ok();
@@ -101,14 +101,14 @@ fn main() {
opts.force_wayland,
detected_wayland
);
run::<display_backend::WaylandBackend>(opts, eww_binary_name)
run::<display_backend::WaylandBackend>(opts, ewwii_binary_name)
} else {
log::debug!(
"Running on X11. force_wayland={}, detected_wayland={}",
opts.force_wayland,
detected_wayland
);
run::<display_backend::X11Backend>(opts, eww_binary_name)
run::<display_backend::X11Backend>(opts, ewwii_binary_name)
};
#[cfg(all(not(feature = "wayland"), feature = "x11"))]
@@ -116,14 +116,14 @@ fn main() {
if use_wayland {
log::warn!("Ewwii compiled without wayland support. Falling back to X11, eventhough wayland was requested.");
}
run::<display_backend::X11Backend>(opts, eww_binary_name)
run::<display_backend::X11Backend>(opts, ewwii_binary_name)
};
#[cfg(all(feature = "wayland", not(feature = "x11")))]
let result = run::<display_backend::WaylandBackend>(opts, eww_binary_name);
let result = run::<display_backend::WaylandBackend>(opts, ewwii_binary_name);
#[cfg(not(any(feature = "wayland", feature = "x11")))]
let result = run::<display_backend::NoBackend>(opts, eww_binary_name);
let result = run::<display_backend::NoBackend>(opts, ewwii_binary_name);
if let Err(err) = result {
error_handling_ctx::print_error(err);
@@ -139,16 +139,16 @@ fn detect_wayland() -> bool {
|| (!wayland_display.is_empty() && !session_type.contains("x11"))
}
fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()> {
fn run<B: DisplayBackend>(opts: opts::Opt, ewwii_binary_name: String) -> Result<()> {
let paths = opts
.config_path
.map(EwwPaths::from_config_dir)
.unwrap_or_else(EwwPaths::default)
.map(EwwiiPaths::from_config_dir)
.unwrap_or_else(EwwiiPaths::default)
.context("Failed to initialize ewwii paths")?;
let should_restart = match &opts.action {
opts::Action::ShellCompletions { .. } => unreachable!(),
opts::Action::Daemon => opts.restart,
opts::Action::Daemon { .. } => opts.restart,
opts::Action::WithServer(action) => opts.restart && action.can_start_daemon(),
opts::Action::ClientOnly(_) => false,
};
@@ -167,24 +167,27 @@ fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()
false
}
// make sure that there isn't already a Eww daemon running.
opts::Action::Daemon if check_server_running(paths.get_ipc_socket_file()) => {
// make sure that there isn't already a Ewwii daemon running.
opts::Action::Daemon { .. } if check_server_running(paths.get_ipc_socket_file()) => {
eprintln!("Ewwii server already running.");
true
}
// initializing the eww server i see..
opts::Action::Daemon => {
opts::Action::Daemon { with_plugin } => {
log::info!("Initializing Ewwii server. ({})", paths.get_ipc_socket_file().display());
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
if !opts.show_logs {
println!(
"Run `{} logs` to see any errors while editing your configuration.",
eww_binary_name
ewwii_binary_name
);
}
let fork_result =
server::initialize_server::<B>(paths.clone(), None, !opts.no_daemonize)?;
let fork_result = server::initialize_server::<B>(
paths.clone(),
None,
!opts.no_daemonize,
with_plugin,
)?;
opts.no_daemonize || fork_result == ForkResult::Parent
}
@@ -217,14 +220,14 @@ fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()
if !opts.show_logs {
println!(
"Run `{} logs` to see any errors while editing your configuration.",
eww_binary_name
ewwii_binary_name
);
}
let (command, response_recv) = action.into_daemon_command();
// start the daemon and give it the command
let fork_result =
server::initialize_server::<B>(paths.clone(), Some(command), true)?;
server::initialize_server::<B>(paths.clone(), Some(command), true, None)?;
let is_parent = fork_result == ForkResult::Parent;
if let (Some(recv), true) = (response_recv, is_parent) {
listen_for_daemon_response(recv);
@@ -259,7 +262,7 @@ fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) {
/// attempt to send a command to the daemon and send it the given action repeatedly.
fn handle_server_command(
paths: &EwwPaths,
paths: &EwwiiPaths,
action: &ActionWithServer,
connect_attempts: usize,
) -> Result<Option<DaemonResponse>> {
@@ -295,7 +298,7 @@ fn attempt_connect(socket_path: impl AsRef<Path>, attempts: usize) -> Option<net
None
}
/// Check if a eww server is currently running by trying to send a ping message to it.
/// Check if a ewwii server is currently running by trying to send a ping message to it.
fn check_server_running(socket_path: impl AsRef<Path>) -> bool {
let response = net::UnixStream::connect(socket_path).ok().and_then(|mut stream| {
client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok()

View File

@@ -67,7 +67,10 @@ pub enum Action {
/// Start the Ewwii daemon.
#[command(name = "daemon", alias = "d")]
Daemon,
Daemon {
#[arg(long)]
with_plugin: Option<String>,
},
#[command(flatten)]
ClientOnly(ActionClientOnly),
@@ -164,13 +167,9 @@ pub enum ActionWithServer {
#[command(name = "close-all", alias = "ca")]
CloseAll,
// /// Prints the variables used in all currently open window
// #[command(name = "state")]
// ShowState {
// /// Shows all variables, including not currently used ones
// #[arg(short, long)]
// all: bool,
// },
/// Prints all the variables in the registery
#[command(name = "state")]
ShowState,
/// List the names of active windows
#[command(name = "list-windows")]
ListWindows,
@@ -188,24 +187,111 @@ pub enum ActionWithServer {
// /// Print out the scope graph structure in graphviz dot format.
// #[command(name = "graph")]
// ShowGraph,
/// Control widgets through CLI.
#[command(name = "widget-control", alias = "wc")]
WidgetControl {
#[command(subcommand)]
action: WidgetControlAction,
},
/// Update the widgets of a particular window. Poll/Listen variables will be cleared
#[command(name = "update", alias = "u")]
TriggerUpdateUI {
/// Inject variables while updating the UI
///
/// Format: --inject-vars foo="val1" baz="val2"
/// Format: --inject foo="val1" baz="val2"
/// Only variables used by the widget tree will affect the UI.
#[arg(long, value_parser = parse_inject_var_map)]
#[arg(long = "inject", short = 'i', value_parser = parse_inject_var_map)]
inject_vars: Option<HashMap<String, String>>,
/// Preserve the new updates.
#[arg(long = "preserve", short = 'p')]
should_preserve_state: bool,
/// Tie the variable lifetime to a window lifetime.
#[arg(long = "lifetime", short = 'l')]
lifetime: Option<String>,
},
/// Call rhai functions. (NOTE: All poll/listen will default to their initial value)
#[command(name = "call-fns")]
CallRhaiFns {
// Rhai functions to call. Format: --fn-calls "fn_name1(args)" "fn_name2(args)"
/// Rhai functions to call. Format: call-fns "fn_name1(args)" "fn_name2(args)"
#[arg(required = true)]
calls: Vec<String>,
},
/// Override the default runtime engine settings
#[command(name = "engine-override")]
EngineOverride {
/// Configuration in JSON format
config_json: String,
/// Weather to print the current engine settings
#[arg(long = "sprint", short = 'p')]
print: bool,
},
/// Set a plugin (.so) to the ewwii binary
#[command(name = "set-plugin")]
SetPlugin {
/// The .so file to load
#[arg(value_parser = absolute_file_path_parser)]
file_path: String,
},
}
/// Subcommands for widget control
#[derive(Subcommand, Debug, Serialize, Deserialize, PartialEq)]
pub enum WidgetControlAction {
/// Remove widget by name
Remove {
/// Names of the widgets to remove
names: Vec<String>,
},
/// Create widgets
Create {
/// Rhai code to create widgets from
rhai_codes: Vec<String>,
/// Name of the widget to add these widgets as a child to
#[arg(long = "parent", short = 'p')]
parent_name: String,
},
/// Update properties of a widget by name
PropertyUpdate {
/// Properties and its value
///
/// Format: value="val1" widget_name="val2"
#[arg(value_parser = parse_inject_var_map)]
property_and_value: HashMap<String, String>,
/// Name of the widget to update the property of
#[arg(long = "widget", short = 'w')]
widget_name: String,
},
/// Add a class to a widget with given name
AddClass {
/// The class to add to the widget
class: String,
/// Name of the widget to add class to
#[arg(long = "widget", short = 'w')]
widget_name: String,
},
/// Remove a class to a widget with given name
RemoveClass {
/// The class to remove from the widget
class: String,
/// Name of the widget to remove class from
#[arg(long = "widget", short = 'w')]
widget_name: String,
},
}
impl Opt {
@@ -267,8 +353,19 @@ impl ActionWithServer {
self,
) -> (app::DaemonCommand, Option<daemon_response::DaemonResponseReceiver>) {
let command = match self {
ActionWithServer::TriggerUpdateUI { inject_vars } => {
app::DaemonCommand::TriggerUpdateUI(inject_vars)
ActionWithServer::WidgetControl { action } => {
return with_response_channel(|sender| app::DaemonCommand::WidgetControl {
action,
sender,
})
}
ActionWithServer::TriggerUpdateUI { inject_vars, should_preserve_state, lifetime } => {
return with_response_channel(|sender| app::DaemonCommand::TriggerUpdateUI {
inject_vars,
should_preserve_state,
lifetime,
sender,
})
}
ActionWithServer::CallRhaiFns { calls } => {
return with_response_channel(|sender| app::DaemonCommand::CallRhaiFns {
@@ -276,7 +373,6 @@ impl ActionWithServer {
sender,
})
}
ActionWithServer::OpenInspector => app::DaemonCommand::OpenInspector,
ActionWithServer::KillServer => app::DaemonCommand::KillServer,
@@ -322,6 +418,9 @@ impl ActionWithServer {
ActionWithServer::Reload => {
return with_response_channel(app::DaemonCommand::ReloadConfigAndCss)
}
ActionWithServer::ShowState => {
return with_response_channel(app::DaemonCommand::ShowState)
}
ActionWithServer::ListWindows => {
return with_response_channel(app::DaemonCommand::ListWindows)
}
@@ -331,6 +430,19 @@ impl ActionWithServer {
ActionWithServer::ShowDebug => {
return with_response_channel(app::DaemonCommand::PrintDebug)
}
ActionWithServer::EngineOverride { config_json, print } => {
return with_response_channel(|sender| app::DaemonCommand::EngineOverride {
config: config_json,
print,
sender,
})
}
ActionWithServer::SetPlugin { file_path } => {
return with_response_channel(|sender| app::DaemonCommand::SetPlugin {
file_path,
sender,
})
}
};
(command, None)
}
@@ -407,3 +519,10 @@ fn parse_inject_var_map(s: &str) -> Result<HashMap<String, String>, String> {
}
Ok(map)
}
fn absolute_file_path_parser(s: &str) -> Result<String, String> {
let p = std::path::Path::new(s);
std::fs::canonicalize(p)
.map_err(|e| format!("Failed to canonicalize '{}': {}", s, e))
.map(|abs_path| abs_path.to_string_lossy().into_owned())
}

View File

@@ -6,17 +6,16 @@ use std::{
use anyhow::{bail, Result};
/// Stores references to all the paths relevant to eww, and abstracts access to these files and directories
/// Stores references to all the paths relevant to ewwii, and abstracts access to these files and directories
#[derive(Debug, Clone)]
pub struct EwwPaths {
pub struct EwwiiPaths {
pub log_file: PathBuf,
pub log_dir: PathBuf,
pub ipc_socket_file: PathBuf,
pub config_dir: PathBuf,
}
// all the eww paths i suppose from the name?
impl EwwPaths {
impl EwwiiPaths {
pub fn from_config_dir<P: AsRef<Path>>(config_dir: P) -> Result<Self> {
let config_dir = config_dir.as_ref();
if config_dir.is_file() {
@@ -55,9 +54,9 @@ impl EwwPaths {
std::fs::create_dir_all(&log_dir)?;
}
Ok(EwwPaths {
Ok(Self {
config_dir,
log_file: log_dir.join(format!("eww_{}.log", daemon_id)),
log_file: log_dir.join(format!("ewwii_{}.log", daemon_id)),
log_dir,
ipc_socket_file,
})
@@ -94,7 +93,7 @@ impl EwwPaths {
}
}
impl std::fmt::Display for EwwPaths {
impl std::fmt::Display for EwwiiPaths {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,

View File

@@ -0,0 +1,86 @@
use ewwii_plugin_api::{rhai_backend, widget_backend, EwwiiAPI};
use rhai::{Array, Dynamic, Engine, EvalAltResult};
use std::sync::mpsc::{channel as mpsc_channel, Receiver, Sender};
pub(crate) struct EwwiiImpl {
pub(crate) requestor: Sender<PluginRequest>,
}
impl EwwiiAPI for EwwiiImpl {
// General
// "PCL = Plugin Controlled Log"
fn print(&self, msg: &str) {
println!("[PCL] {}", msg);
}
fn log(&self, msg: &str) {
log::info!("[PCL] {}", msg);
}
fn warn(&self, msg: &str) {
log::warn!("[PCL] {}", msg);
}
fn error(&self, msg: &str) {
log::error!("[PCL] {}", msg);
}
// Rhai Manipulation Stuff
fn rhai_engine_action(&self, f: Box<dyn FnOnce(&mut Engine) + Send>) -> Result<(), String> {
self.requestor
.send(PluginRequest::RhaiEngineAct(f))
.map_err(|_| "Failed to send request to host".to_string())?;
Ok(())
}
fn register_function(
&self,
name: String,
namespace: rhai_backend::RhaiFnNamespace,
f: Box<dyn Fn(Array) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync>,
) -> Result<(), String> {
let func_info = (name, namespace, f);
self.requestor
.send(PluginRequest::RegisterFunc(func_info))
.map_err(|_| "Failed to send request to host".to_string())?;
Ok(())
}
// Widget Rendering & Logic
fn list_widget_ids(&self) -> Result<Vec<u64>, String> {
let (tx, rx): (Sender<Vec<u64>>, Receiver<Vec<u64>>) = mpsc_channel();
self.requestor
.send(PluginRequest::ListWidgetIds(tx))
.map_err(|_| "Failed to send request to host".to_string())?;
match rx.recv() {
Ok(r) => Ok(r),
Err(e) => Err(e.to_string()),
}
}
fn widget_reg_action(
&self,
f: Box<dyn FnOnce(&mut widget_backend::WidgetRegistryRepr) + Send>,
) -> Result<(), String> {
self.requestor
.send(PluginRequest::WidgetRegistryAct(f))
.map_err(|_| "Failed to send request to host".to_string())?;
Ok(())
}
}
pub(crate) enum PluginRequest {
RhaiEngineAct(Box<dyn FnOnce(&mut Engine) + Send>),
RegisterFunc(
(
String,
rhai_backend::RhaiFnNamespace,
Box<dyn Fn(Array) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync>,
),
),
ListWidgetIds(Sender<Vec<u64>>),
WidgetRegistryAct(Box<dyn FnOnce(&mut widget_backend::WidgetRegistryRepr) + Send>),
}

View File

@@ -1,27 +1,28 @@
use crate::{
app::{self, App, DaemonCommand},
app::{self, App, DaemonCommand, EngineConfValues},
config, daemon_response,
display_backend::DisplayBackend,
error_handling_ctx, ipc_server, EwwPaths,
error_handling_ctx, ipc_server, EwwiiPaths,
};
use anyhow::{Context, Result};
use gtk4::prelude::{DisplayExt, ListModelExt};
use std::{
// cell::RefCell,
cell::RefCell,
collections::{HashMap, HashSet},
io::Write,
marker::PhantomData,
os::unix::io::AsRawFd,
path::Path,
// rc::Rc,
sync::{atomic::Ordering, Arc},
rc::Rc,
sync::{atomic::Ordering, Arc, RwLock},
};
use tokio::sync::mpsc::*;
pub fn initialize_server<B: DisplayBackend>(
paths: EwwPaths,
paths: EwwiiPaths,
action: Option<DaemonCommand>,
should_daemonize: bool,
ewwii_plugin_path: Option<String>,
) -> Result<ForkResult> {
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
@@ -31,18 +32,11 @@ pub fn initialize_server<B: DisplayBackend>(
log::info!("Loading paths: {}", &paths);
let read_config = config::read_from_ewwii_paths(&paths);
let pl_handler_store: rhai_impl::updates::ReactiveVarStore =
Arc::new(RwLock::new(HashMap::new()));
let ewwii_config = match read_config {
Ok(config) => config,
Err(err) => {
error_handling_ctx::print_error(err);
config::EwwiiConfig::default()
// TODO: Maybe do something so that we can exit if user wants.
// std::process::exit(1);
}
};
let config_parser =
Rc::new(RefCell::new(rhai_impl::parser::ParseConfig::new(Some(pl_handler_store.clone()))));
cleanup_log_dir(paths.get_log_dir())?;
@@ -76,28 +70,57 @@ pub fn initialize_server<B: DisplayBackend>(
if B::IS_WAYLAND {
std::env::set_var("GDK_BACKEND", "wayland")
}
gtk::init()?;
gtk4::init()?;
let main_loop = gtk4::glib::MainLoop::new(None, false);
let mut app: App<B> = app::App {
ewwii_config,
ewwii_config: config::EwwiiConfig::default(),
open_windows: HashMap::new(),
failed_windows: HashSet::new(),
instance_id_to_args: HashMap::new(),
css_provider: gtk::CssProvider::new(),
css_provider: gtk4::CssProvider::new(),
reloading: false,
app_evt_send: ui_send.clone(),
window_close_timer_abort_senders: HashMap::new(),
widget_reg_store: std::rc::Rc::new(std::sync::Mutex::new(None)),
pl_handler_store: None,
pl_handler_store,
clear_pl_onclose: HashMap::new(),
rt_engine_config: EngineConfValues::default(),
config_parser,
paths,
gtk_main_loop: main_loop.clone(),
phantom: PhantomData,
};
if let Some(screen) = gtk::gdk::Screen::default() {
gtk::StyleContext::add_provider_for_screen(
&screen,
&app.css_provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
// start up plugins
if let Some(ewwii_plugin) = ewwii_plugin_path {
if let Err(e) = app.set_ewwii_plugin(ewwii_plugin) {
error_handling_ctx::print_error(e);
}
}
let mut config_parser_mut = app.config_parser.borrow_mut();
let read_config = config::read_from_ewwii_paths(&app.paths, &mut *config_parser_mut);
// free the temporary parser borrow
drop(config_parser_mut);
match read_config {
Ok(new_config) => {
app.ewwii_config = new_config;
}
Err(err) => {
error_handling_ctx::print_error(err);
// TODO: Maybe do something so that we can exit if user wants.
// std::process::exit(1);
}
};
if let Some(display) = gtk4::gdk::Display::default() {
gtk4::style_context_add_provider_for_display(&display, &app.css_provider, 900);
}
if let Ok((file_id, css)) = config::scss::parse_scss_from_config(app.paths.get_config_dir()) {
@@ -111,7 +134,7 @@ pub fn initialize_server<B: DisplayBackend>(
// initialize all the handlers and tasks running asyncronously
let tokio_handle = init_async_part(app.paths.clone(), ui_send);
gtk::glib::MainContext::default().spawn_local(async move {
gtk4::glib::MainContext::default().spawn_local(async move {
// if an action was given to the daemon initially, execute it first.
if let Some(action) = action {
app.handle_command(action).await;
@@ -133,20 +156,26 @@ pub fn initialize_server<B: DisplayBackend>(
// allow the GTK main thread to do tokio things
let _g = tokio_handle.enter();
gtk::main();
main_loop.run();
log::info!("main application thread finished");
Ok(ForkResult::Child)
}
fn connect_monitor_added(ui_send: UnboundedSender<DaemonCommand>) {
let display = gtk::gdk::Display::default().expect("could not get default display");
display.connect_monitor_added({
move |_display: &gtk::gdk::Display, _monitor: &gtk::gdk::Monitor| {
log::info!("New monitor connected, reloading configuration");
let _ = reload_config_and_css(&ui_send);
}
});
if let Some(display) = gtk4::gdk::Display::default() {
let monitors = display.monitors();
monitors.connect_items_changed(gtk4::glib::clone!(
#[strong]
ui_send,
move |_, _, _, _| {
let _ = reload_config_and_css(&ui_send);
}
));
} else {
log::warn!("Cannot access GDK Display on this session (likely Wayland)");
}
}
fn reload_config_and_css(ui_send: &UnboundedSender<DaemonCommand>) -> Result<()> {
@@ -165,7 +194,7 @@ fn reload_config_and_css(ui_send: &UnboundedSender<DaemonCommand>) -> Result<()>
}
fn init_async_part(
paths: EwwPaths,
paths: EwwiiPaths,
ui_send: UnboundedSender<app::DaemonCommand>,
) -> tokio::runtime::Handle {
let rt = tokio::runtime::Builder::new_multi_thread()
@@ -316,7 +345,7 @@ fn do_detach(log_file_path: impl AsRef<Path>) -> Result<ForkResult> {
/// Ensure the log directory never grows larger than 100MB by deleting files older than 7 days,
/// and truncating all other logfiles to 100MB.
fn cleanup_log_dir(log_dir: impl AsRef<Path>) -> Result<()> {
// Find all files named "eww_*.log" in the log directory
// Find all files named "ewwii_*.log" in the log directory
let log_files = std::fs::read_dir(&log_dir)?
.filter_map(|entry| {
let entry = entry.ok()?;

View File

@@ -1,7 +1,8 @@
use anyhow::Result;
use gtk::gdk::prelude::Cast;
use gtk4::gdk::prelude::Cast;
use crate::{config::WindowDefinition, widgets::widget_definitions::*};
use crate::config::WindowDefinition;
use crate::widgets::widget_definitions::*;
use rhai_impl::ast::WidgetNode;
@@ -17,7 +18,7 @@ pub enum WidgetInput<'a> {
pub fn build_gtk_widget<'a>(
input: &'a WidgetInput<'a>,
widget_reg: &mut WidgetRegistry,
) -> Result<gtk::Widget> {
) -> Result<gtk4::Widget> {
let node: &'a WidgetNode = match input {
WidgetInput::Node(n) => n,
WidgetInput::BorrowedNode(n) => n,
@@ -30,7 +31,7 @@ pub fn build_gtk_widget<'a>(
fn build_gtk_widget_from_node(
root_node: &WidgetNode,
widget_reg: &mut WidgetRegistry,
) -> Result<gtk::Widget> {
) -> Result<gtk4::Widget> {
/*
When a a new widget is added to the build process,
make sure to update get_id_to_props_map() found in
@@ -40,23 +41,30 @@ fn build_gtk_widget_from_node(
let gtk_widget = match root_node {
WidgetNode::Box { props, children } => build_gtk_box(props, children, widget_reg)?.upcast(),
WidgetNode::CenterBox { props, children } => {
build_center_box(props, children, widget_reg)?.upcast()
WidgetNode::FlowBox { props, children } => {
build_gtk_flowbox(props, children, widget_reg)?.upcast()
}
WidgetNode::EventBox { props, children } => {
build_gtk_event_box(props, children, widget_reg)?.upcast()
build_event_box(props, children, widget_reg)?.upcast()
}
WidgetNode::ToolTip { props, children } => {
build_tooltip(props, children, widget_reg)?.upcast()
}
WidgetNode::LocalBind { props, children } => {
build_localbind_util(props, children, widget_reg)?.upcast()
}
WidgetNode::WidgetAction { props, children } => {
build_widgetaction_util(props, children, widget_reg)?.upcast()
}
WidgetNode::CircularProgress { props } => {
build_circular_progress_bar(props, widget_reg)?.upcast()
}
WidgetNode::Graph { props } => build_graph(props, widget_reg)?.upcast(),
WidgetNode::Transform { props } => build_transform(props, widget_reg)?.upcast(),
WidgetNode::Slider { props } => build_gtk_scale(props, widget_reg)?.upcast(),
WidgetNode::GtkUI { props } => build_gtk_ui_file(props)?.upcast(),
// WidgetNode::Graph { props } => build_graph(props, widget_reg)?.upcast(),
// WidgetNode::Transform { props } => build_transform(props, widget_reg)?.upcast(),
WidgetNode::Scale { props } => build_gtk_scale(props, widget_reg)?.upcast(),
WidgetNode::Progress { props } => build_gtk_progress(props, widget_reg)?.upcast(),
WidgetNode::Image { props } => build_gtk_image(props, widget_reg)?.upcast(),
WidgetNode::Image { props } => build_image(props, widget_reg)?.upcast(),
WidgetNode::Button { props } => build_gtk_button(props, widget_reg)?.upcast(),
WidgetNode::Label { props } => build_gtk_label(props, widget_reg)?.upcast(),
// WIDGET_NAME_LITERAL => build_gtk_literal(node)?.upcast(),

View File

@@ -1,206 +1,124 @@
use anyhow::{anyhow, Result};
use gtk::glib::{self, object_subclass, prelude::*, wrapper, Properties};
use gtk::{cairo, gdk, prelude::*, subclass::prelude::*};
use std::cell::RefCell;
use glib::Object;
use gtk4::glib;
use gtk4::prelude::*;
use gtk4::subclass::prelude::*;
use gtk4::{cairo, gdk, graphene};
use std::cell::Cell;
use crate::error_handling_ctx;
mod imp {
use super::*;
wrapper! {
pub struct CircProg(ObjectSubclass<CircProgPriv>)
@extends gtk::Bin, gtk::Container, gtk::Widget;
}
#[derive(Properties)]
#[properties(wrapper_type = CircProg)]
pub struct CircProgPriv {
#[property(
get,
set,
nick = "Starting at",
blurb = "Starting at",
minimum = 0f64,
maximum = 100f64,
default = 0f64
)]
start_at: RefCell<f64>,
#[property(
get,
set,
nick = "Value",
blurb = "The value",
minimum = 0f64,
maximum = 100f64,
default = 0f64
)]
value: RefCell<f64>,
#[property(
get,
set,
nick = "Thickness",
blurb = "Thickness",
minimum = 0f64,
maximum = 100f64,
default = 1f64
)]
thickness: RefCell<f64>,
#[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)]
clockwise: RefCell<bool>,
content: RefCell<Option<gtk::Widget>>,
}
// This should match the default values from the ParamSpecs
impl Default for CircProgPriv {
fn default() -> Self {
CircProgPriv {
start_at: RefCell::new(0.0),
value: RefCell::new(0.0),
thickness: RefCell::new(1.0),
clockwise: RefCell::new(true),
content: RefCell::new(None),
}
}
}
impl ObjectImpl for CircProgPriv {
fn properties() -> &'static [glib::ParamSpec] {
Self::derived_properties()
pub struct CircProg {
pub value: Cell<f64>,
pub start_at: Cell<f64>,
pub thickness: Cell<f64>,
pub clockwise: Cell<bool>,
pub fg_color: Cell<gdk::RGBA>,
pub bg_color: Cell<gdk::RGBA>,
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"value" => {
self.value.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
impl Default for CircProg {
fn default() -> Self {
Self {
value: Cell::new(0.0),
start_at: Cell::new(0.0),
thickness: Cell::new(8.0),
clockwise: Cell::new(true),
fg_color: Cell::new(gdk::RGBA::new(1.0, 0.0, 0.0, 1.0)),
bg_color: Cell::new(gdk::RGBA::new(0.0, 0.0, 0.0, 0.1)),
}
"thickness" => {
self.thickness.replace(value.get().unwrap());
}
}
#[glib::object_subclass]
impl ObjectSubclass for CircProg {
const NAME: &'static str = "CircProg";
type Type = super::CircProg;
type ParentType = gtk4::Widget;
}
impl ObjectImpl for CircProg {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_css_class("circular-progress");
}
fn properties() -> &'static [glib::ParamSpec] {
use once_cell::sync::Lazy;
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecDouble::builder("value")
.minimum(0.0)
.maximum(100.0)
.default_value(0.0)
.build(),
glib::ParamSpecDouble::builder("start-at")
.minimum(0.0)
.maximum(100.0)
.default_value(0.0)
.build(),
glib::ParamSpecDouble::builder("thickness")
.minimum(1.0)
.maximum(50.0)
.default_value(8.0)
.build(),
glib::ParamSpecBoolean::builder("clockwise").default_value(true).build(),
glib::ParamSpecBoxed::builder::<gdk::RGBA>("fg-color").build(),
glib::ParamSpecBoxed::builder::<gdk::RGBA>("bg-color").build(),
]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"value" => self.value.set(value.get().unwrap()),
"start-at" => self.start_at.set(value.get().unwrap()),
"thickness" => self.thickness.set(value.get().unwrap()),
"clockwise" => self.clockwise.set(value.get().unwrap()),
"fg-color" => self.fg_color.set(value.get().unwrap()),
"bg-color" => self.bg_color.set(value.get().unwrap()),
x => panic!("Tried to set inexistant property of CircProg: {}", x,),
}
"start-at" => {
self.start_at.replace(value.get().unwrap());
self.obj().queue_draw();
}
fn property(&self, _: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"value" => self.value.get().to_value(),
"start-at" => self.start_at.get().to_value(),
"thickness" => self.thickness.get().to_value(),
"clockwise" => self.clockwise.get().to_value(),
"fg-color" => self.fg_color.get().to_value(),
"bg-color" => self.bg_color.get().to_value(),
x => panic!("Tried to get inexistant property of CircProg: {}", x,),
}
"clockwise" => {
self.clockwise.replace(value.get().unwrap());
}
x => panic!("Tried to set inexistant property of CircProg: {}", x,),
}
}
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.derived_property(id, pspec)
}
}
#[object_subclass]
impl ObjectSubclass for CircProgPriv {
type ParentType = gtk::Bin;
type Type = CircProg;
const NAME: &'static str = "CircProg";
fn class_init(klass: &mut Self::Class) {
klass.set_css_name("circular-progress");
}
}
impl Default for CircProg {
fn default() -> Self {
Self::new()
}
}
impl CircProg {
pub fn new() -> Self {
glib::Object::new::<Self>()
}
}
impl ContainerImpl for CircProgPriv {
fn add(&self, widget: &gtk::Widget) {
if let Some(content) = &*self.content.borrow() {
// TODO: Handle this error when populating children widgets instead
error_handling_ctx::print_error(anyhow!(
"Error, trying to add multiple children to a circular-progress widget"
));
self.parent_remove(content);
impl WidgetImpl for CircProg {
fn measure(&self, _orientation: gtk4::Orientation, _for_size: i32) -> (i32, i32, i32, i32) {
let min_size = 32;
let natural_size = 64;
(min_size, natural_size, -1, -1)
}
self.parent_add(widget);
self.content.replace(Some(widget.clone()));
}
}
fn calc_widget_lowest_preferred_dimension(widget: &gtk::Widget) -> (i32, i32) {
let preferred_width = widget.preferred_width();
let preferred_height = widget.preferred_height();
let min_lowest = i32::min(preferred_width.0, preferred_height.0);
let natural_lowest = i32::min(preferred_width.1, preferred_height.1);
(min_lowest, natural_lowest)
}
fn snapshot(&self, snapshot: &gtk4::Snapshot) {
let value = self.value.get();
let start_at = self.start_at.get();
let thickness = self.thickness.get();
let clockwise = self.clockwise.get();
let fg_color = self.fg_color.get();
let bg_color = self.bg_color.get();
impl BinImpl for CircProgPriv {}
impl WidgetImpl for CircProgPriv {
// We overwrite preferred_* so that overflowing content from the children gets cropped
// We return min(child_width, child_height)
fn preferred_width(&self) -> (i32, i32) {
let styles = self.obj().style_context();
let margin = styles.margin(gtk::StateFlags::NORMAL);
if let Some(child) = &*self.content.borrow() {
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
(
min_child + margin.right as i32 + margin.left as i32,
natural_child + margin.right as i32 + margin.left as i32,
)
} else {
let empty_width =
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
(empty_width, empty_width)
}
}
fn preferred_width_for_height(&self, _height: i32) -> (i32, i32) {
self.preferred_width()
}
fn preferred_height(&self) -> (i32, i32) {
let styles = self.obj().style_context();
let margin = styles.margin(gtk::StateFlags::NORMAL);
if let Some(child) = &*self.content.borrow() {
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
(
min_child + margin.bottom as i32 + margin.top as i32,
natural_child + margin.bottom as i32 + margin.top as i32,
)
} else {
let empty_height =
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
(empty_height, empty_height)
}
}
fn preferred_height_for_width(&self, _width: i32) -> (i32, i32) {
self.preferred_height()
}
fn draw(&self, cr: &cairo::Context) -> glib::Propagation {
let res: Result<()> = (|| {
let value = *self.value.borrow();
let start_at = *self.start_at.borrow();
let thickness = *self.thickness.borrow();
let clockwise = *self.clockwise.borrow();
let styles = self.obj().style_context();
let margin = styles.margin(gtk::StateFlags::NORMAL);
let margin_start = self.obj().margin_start() as f64;
let margin_end = self.obj().margin_end() as f64;
let margin_top = self.obj().margin_top() as f64;
let margin_bottom = self.obj().margin_bottom() as f64;
// Padding is not supported yet
let fg_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL);
let bg_color: gdk::RGBA = styles
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
.get()?;
let (start_angle, end_angle) = if clockwise {
(0.0, perc_to_rad(value))
} else {
@@ -211,12 +129,20 @@ impl WidgetImpl for CircProgPriv {
let total_height = self.obj().allocated_height() as f64;
let center = (total_width / 2.0, total_height / 2.0);
let circle_width = total_width - margin.left as f64 - margin.right as f64;
let circle_height = total_height - margin.top as f64 - margin.bottom as f64;
let circle_width = total_width - margin_start - margin_end;
let circle_height = total_height - margin_top - margin_bottom;
let outer_ring = f64::min(circle_width, circle_height) / 2.0;
let inner_ring = (f64::min(circle_width, circle_height) / 2.0) - thickness;
cr.save()?;
// Snapshot Cairo node
let cr = snapshot.append_cairo(&graphene::Rect::new(
0.0_f32,
0.0_f32,
total_width as f32,
total_height as f32,
));
cr.save().unwrap();
// Centering
cr.translate(center.0, center.1);
@@ -226,45 +152,45 @@ impl WidgetImpl for CircProgPriv {
// Background Ring
cr.move_to(center.0, center.1);
cr.arc(center.0, center.1, outer_ring, 0.0, perc_to_rad(100.0));
cr.set_source_rgba(bg_color.red(), bg_color.green(), bg_color.blue(), bg_color.alpha());
cr.set_source_rgba(
bg_color.red().into(),
bg_color.green().into(),
bg_color.blue().into(),
bg_color.alpha().into(),
);
cr.move_to(center.0, center.1);
cr.arc(center.0, center.1, inner_ring, 0.0, perc_to_rad(100.0));
cr.set_fill_rule(cairo::FillRule::EvenOdd); // Substract one circle from the other
cr.fill()?;
cr.fill().unwrap();
// Foreground Ring
cr.move_to(center.0, center.1);
cr.arc(center.0, center.1, outer_ring, start_angle, end_angle);
cr.set_source_rgba(fg_color.red(), fg_color.green(), fg_color.blue(), fg_color.alpha());
cr.set_source_rgba(
fg_color.red().into(),
fg_color.green().into(),
fg_color.blue().into(),
fg_color.alpha().into(),
);
cr.move_to(center.0, center.1);
cr.arc(center.0, center.1, inner_ring, start_angle, end_angle);
cr.set_fill_rule(cairo::FillRule::EvenOdd); // Substract one circle from the other
cr.fill()?;
cr.restore()?;
cr.fill().unwrap();
// Draw the children widget, clipping it to the inside
if let Some(child) = &*self.content.borrow() {
cr.save()?;
cr.restore().unwrap();
}
}
}
// Center circular clip
cr.arc(center.0, center.1, inner_ring + 1.0, 0.0, perc_to_rad(100.0));
cr.set_source_rgba(bg_color.red(), 0.0, 0.0, bg_color.alpha());
cr.clip();
glib::wrapper! {
pub struct CircProg(ObjectSubclass<imp::CircProg>)
@extends gtk4::Widget,
@implements gtk4::Accessible, gtk4::Actionable, gtk4::Buildable, gtk4::ConstraintTarget;
}
// Children widget
self.obj().propagate_draw(child, cr);
cr.reset_clip();
cr.restore()?;
}
Ok(())
})();
if let Err(error) = res {
error_handling_ctx::print_error(error)
};
glib::Propagation::Proceed
impl CircProg {
pub fn new() -> Self {
Object::builder().build()
}
}

View File

@@ -1,349 +1,349 @@
use std::{cell::RefCell, collections::VecDeque};
// https://www.figuiere.net/technotes/notes/tn002/
// https://github.com/gtk-rs/examples/blob/master/src/bin/listbox_model.rs
use anyhow::{anyhow, Result};
use gtk::glib::{self, object_subclass, wrapper, Properties};
use gtk::{cairo, gdk, prelude::*, subclass::prelude::*};
// use std::{cell::RefCell, collections::VecDeque};
// // https://www.figuiere.net/technotes/notes/tn002/
// // https://github.com/gtk-rs/examples/blob/master/src/bin/listbox_model.rs
// use anyhow::{anyhow, Result};
// use gtk4::glib::{self, object_subclass, wrapper, Properties};
// use gtk4::{cairo, gdk, prelude::*, subclass::prelude::*};
use crate::error_handling_ctx;
// use crate::error_handling_ctx;
// This widget shouldn't be a Bin/Container but I've not been
// able to subclass just a gtk::Widget
wrapper! {
pub struct Graph(ObjectSubclass<GraphPriv>)
@extends gtk::Bin, gtk::Container, gtk::Widget;
}
// // This widget shouldn't be a Bin/Container but I've not been
// // able to subclass just a gtk4::Widget
// wrapper! {
// pub struct Graph(ObjectSubclass<GraphPriv>)
// @extends gtk4::Bin, gtk4::Container, gtk4::Widget;
// }
#[derive(Properties)]
#[properties(wrapper_type = Graph)]
pub struct GraphPriv {
#[property(get, set, nick = "Value", blurb = "The value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
value: RefCell<f64>,
// #[derive(Properties)]
// #[properties(wrapper_type = Graph)]
// pub struct GraphPriv {
// #[property(get, set, nick = "Value", blurb = "The value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
// value: RefCell<f64>,
#[property(get, set, nick = "Thickness", blurb = "The Thickness", minimum = 0f64, maximum = f64::MAX, default = 1f64)]
thickness: RefCell<f64>,
// #[property(get, set, nick = "Thickness", blurb = "The Thickness", minimum = 0f64, maximum = f64::MAX, default = 1f64)]
// thickness: RefCell<f64>,
#[property(get, set, nick = "Line Style", blurb = "The Line Style", default = "miter")]
line_style: RefCell<String>,
// #[property(get, set, nick = "Line Style", blurb = "The Line Style", default = "miter")]
// line_style: RefCell<String>,
#[property(get, set, nick = "Maximum Value", blurb = "The Maximum Value", minimum = 0f64, maximum = f64::MAX, default = 100f64)]
min: RefCell<f64>,
// #[property(get, set, nick = "Maximum Value", blurb = "The Maximum Value", minimum = 0f64, maximum = f64::MAX, default = 100f64)]
// min: RefCell<f64>,
#[property(get, set, nick = "Minumum Value", blurb = "The Minimum Value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
max: RefCell<f64>,
// #[property(get, set, nick = "Minumum Value", blurb = "The Minimum Value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
// max: RefCell<f64>,
#[property(get, set, nick = "Dynamic", blurb = "If it is dynamic", default = true)]
dynamic: RefCell<bool>,
// #[property(get, set, nick = "Dynamic", blurb = "If it is dynamic", default = true)]
// dynamic: RefCell<bool>,
#[property(get, set, nick = "Time Range", blurb = "The Time Range", minimum = 0u64, maximum = u64::MAX, default = 10u64)]
time_range: RefCell<u64>,
// #[property(get, set, nick = "Time Range", blurb = "The Time Range", minimum = 0u64, maximum = u64::MAX, default = 10u64)]
// time_range: RefCell<u64>,
#[property(get, set, nick = "Flip X", blurb = "Flip the x axis", default = true)]
flip_x: RefCell<bool>,
#[property(get, set, nick = "Flip Y", blurb = "Flip the y axis", default = true)]
flip_y: RefCell<bool>,
#[property(get, set, nick = "Vertical", blurb = "Exchange the x and y axes", default = false)]
vertical: RefCell<bool>,
// #[property(get, set, nick = "Flip X", blurb = "Flip the x axis", default = true)]
// flip_x: RefCell<bool>,
// #[property(get, set, nick = "Flip Y", blurb = "Flip the y axis", default = true)]
// flip_y: RefCell<bool>,
// #[property(get, set, nick = "Vertical", blurb = "Exchange the x and y axes", default = false)]
// vertical: RefCell<bool>,
history: RefCell<VecDeque<(std::time::Instant, f64)>>,
extra_point: RefCell<Option<(std::time::Instant, f64)>>,
last_updated_at: RefCell<std::time::Instant>,
}
// history: RefCell<VecDeque<(std::time::Instant, f64)>>,
// extra_point: RefCell<Option<(std::time::Instant, f64)>>,
// last_updated_at: RefCell<std::time::Instant>,
// }
impl Default for GraphPriv {
fn default() -> Self {
Self {
value: RefCell::new(0.0),
thickness: RefCell::new(1.0),
line_style: RefCell::new("miter".to_string()),
min: RefCell::new(0.0),
max: RefCell::new(100.0),
dynamic: RefCell::new(true),
time_range: RefCell::new(10),
flip_x: RefCell::new(true),
flip_y: RefCell::new(true),
vertical: RefCell::new(false),
history: RefCell::new(VecDeque::new()),
extra_point: RefCell::new(None),
last_updated_at: RefCell::new(std::time::Instant::now()),
}
}
}
// impl Default for GraphPriv {
// fn default() -> Self {
// Self {
// value: RefCell::new(0.0),
// thickness: RefCell::new(1.0),
// line_style: RefCell::new("miter".to_string()),
// min: RefCell::new(0.0),
// max: RefCell::new(100.0),
// dynamic: RefCell::new(true),
// time_range: RefCell::new(10),
// flip_x: RefCell::new(true),
// flip_y: RefCell::new(true),
// vertical: RefCell::new(false),
// history: RefCell::new(VecDeque::new()),
// extra_point: RefCell::new(None),
// last_updated_at: RefCell::new(std::time::Instant::now()),
// }
// }
// }
impl GraphPriv {
// Updates the history, removing points ouside the range
fn update_history(&self, v: (std::time::Instant, f64)) {
let mut history = self.history.borrow_mut();
let mut last_value = self.extra_point.borrow_mut();
let mut last_updated_at = self.last_updated_at.borrow_mut();
*last_updated_at = std::time::Instant::now();
// impl GraphPriv {
// // Updates the history, removing points ouside the range
// fn update_history(&self, v: (std::time::Instant, f64)) {
// let mut history = self.history.borrow_mut();
// let mut last_value = self.extra_point.borrow_mut();
// let mut last_updated_at = self.last_updated_at.borrow_mut();
// *last_updated_at = std::time::Instant::now();
while let Some(entry) = history.front() {
if last_updated_at.duration_since(entry.0).as_millis() as u64
> *self.time_range.borrow()
{
*last_value = history.pop_front();
} else {
break;
}
}
history.push_back(v);
}
/**
* Receives normalized (0-1) coordinates `x` and `y` and convert them to the
* point on the widget.
*/
fn value_to_point(&self, width: f64, height: f64, x: f64, y: f64) -> (f64, f64) {
let x = if *self.flip_x.borrow() { 1.0 - x } else { x };
let y = if *self.flip_y.borrow() { 1.0 - y } else { y };
let (x, y) = if *self.vertical.borrow() { (y, x) } else { (x, y) };
(width * x, height * y)
}
}
// while let Some(entry) = history.front() {
// if last_updated_at.duration_since(entry.0).as_millis() as u64
// > *self.time_range.borrow()
// {
// *last_value = history.pop_front();
// } else {
// break;
// }
// }
// history.push_back(v);
// }
// /**
// * Receives normalized (0-1) coordinates `x` and `y` and convert them to the
// * point on the widget.
// */
// fn value_to_point(&self, width: f64, height: f64, x: f64, y: f64) -> (f64, f64) {
// let x = if *self.flip_x.borrow() { 1.0 - x } else { x };
// let y = if *self.flip_y.borrow() { 1.0 - y } else { y };
// let (x, y) = if *self.vertical.borrow() { (y, x) } else { (x, y) };
// (width * x, height * y)
// }
// }
impl ObjectImpl for GraphPriv {
fn properties() -> &'static [glib::ParamSpec] {
Self::derived_properties()
}
// impl ObjectImpl for GraphPriv {
// fn properties() -> &'static [glib::ParamSpec] {
// Self::derived_properties()
// }
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"value" => {
let value = value.get().unwrap();
self.value.replace(value);
self.update_history((std::time::Instant::now(), value));
self.obj().queue_draw();
}
"thickness" => {
self.thickness.replace(value.get().unwrap());
}
"max" => {
self.max.replace(value.get().unwrap());
}
"min" => {
self.min.replace(value.get().unwrap());
}
"dynamic" => {
self.dynamic.replace(value.get().unwrap());
}
"time-range" => {
self.time_range.replace(value.get().unwrap());
}
"line-style" => {
self.line_style.replace(value.get().unwrap());
}
"flip-x" => {
self.flip_x.replace(value.get().unwrap());
}
"flip-y" => {
self.flip_y.replace(value.get().unwrap());
}
"vertical" => {
self.vertical.replace(value.get().unwrap());
}
x => panic!("Tried to set inexistant property of Graph: {}", x,),
}
}
// fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
// match pspec.name() {
// "value" => {
// let value = value.get().unwrap();
// self.value.replace(value);
// self.update_history((std::time::Instant::now(), value));
// self.obj().queue_draw();
// }
// "thickness" => {
// self.thickness.replace(value.get().unwrap());
// }
// "max" => {
// self.max.replace(value.get().unwrap());
// }
// "min" => {
// self.min.replace(value.get().unwrap());
// }
// "dynamic" => {
// self.dynamic.replace(value.get().unwrap());
// }
// "time-range" => {
// self.time_range.replace(value.get().unwrap());
// }
// "line-style" => {
// self.line_style.replace(value.get().unwrap());
// }
// "flip-x" => {
// self.flip_x.replace(value.get().unwrap());
// }
// "flip-y" => {
// self.flip_y.replace(value.get().unwrap());
// }
// "vertical" => {
// self.vertical.replace(value.get().unwrap());
// }
// x => panic!("Tried to set inexistant property of Graph: {}", x,),
// }
// }
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.derived_property(id, pspec)
}
}
// fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
// self.derived_property(id, pspec)
// }
// }
#[object_subclass]
impl ObjectSubclass for GraphPriv {
type ParentType = gtk::Bin;
type Type = Graph;
// #[object_subclass]
// impl ObjectSubclass for GraphPriv {
// type ParentType = gtk4::Bin;
// type Type = Graph;
const NAME: &'static str = "Graph";
// const NAME: &'static str = "Graph";
fn class_init(klass: &mut Self::Class) {
klass.set_css_name("graph");
}
}
// fn class_init(klass: &mut Self::Class) {
// klass.set_css_name("graph");
// }
// }
impl Default for Graph {
fn default() -> Self {
Self::new()
}
}
// impl Default for Graph {
// fn default() -> Self {
// Self::new()
// }
// }
impl Graph {
pub fn new() -> Self {
glib::Object::new::<Self>()
}
}
// impl Graph {
// pub fn new() -> Self {
// glib::Object::new::<Self>()
// }
// }
impl ContainerImpl for GraphPriv {
fn add(&self, _widget: &gtk::Widget) {
error_handling_ctx::print_error(anyhow!("Error, Graph widget shoudln't have any children"));
}
}
// impl ContainerImpl for GraphPriv {
// fn add(&self, _widget: &gtk4::Widget) {
// error_handling_ctx::print_error(anyhow!("Error, Graph widget shoudln't have any children"));
// }
// }
impl BinImpl for GraphPriv {}
impl WidgetImpl for GraphPriv {
fn preferred_width(&self) -> (i32, i32) {
let thickness = *self.thickness.borrow() as i32;
(thickness, thickness)
}
// impl BinImpl for GraphPriv {}
// impl WidgetImpl for GraphPriv {
// fn preferred_width(&self) -> (i32, i32) {
// let thickness = *self.thickness.borrow() as i32;
// (thickness, thickness)
// }
fn preferred_width_for_height(&self, height: i32) -> (i32, i32) {
(height, height)
}
// fn preferred_width_for_height(&self, height: i32) -> (i32, i32) {
// (height, height)
// }
fn preferred_height(&self) -> (i32, i32) {
let thickness = *self.thickness.borrow() as i32;
(thickness, thickness)
}
// fn preferred_height(&self) -> (i32, i32) {
// let thickness = *self.thickness.borrow() as i32;
// (thickness, thickness)
// }
fn preferred_height_for_width(&self, width: i32) -> (i32, i32) {
(width, width)
}
// fn preferred_height_for_width(&self, width: i32) -> (i32, i32) {
// (width, width)
// }
fn draw(&self, cr: &cairo::Context) -> glib::Propagation {
let res: Result<()> = (|| {
let history = &*self.history.borrow();
let extra_point = *self.extra_point.borrow();
// fn draw(&self, cr: &cairo::Context) -> glib::Propagation {
// let res: Result<()> = (|| {
// let history = &*self.history.borrow();
// let extra_point = *self.extra_point.borrow();
// Calculate the max value
let (min, max) = {
let mut max = *self.max.borrow();
let min = *self.min.borrow();
let dynamic = *self.dynamic.borrow();
if dynamic {
// Check for points higher than max
for (_, value) in history {
if *value > max {
max = *value;
}
}
if let Some((_, value)) = extra_point {
if value > max {
max = value;
}
}
}
(min, max)
};
// // Calculate the max value
// let (min, max) = {
// let mut max = *self.max.borrow();
// let min = *self.min.borrow();
// let dynamic = *self.dynamic.borrow();
// if dynamic {
// // Check for points higher than max
// for (_, value) in history {
// if *value > max {
// max = *value;
// }
// }
// if let Some((_, value)) = extra_point {
// if value > max {
// max = value;
// }
// }
// }
// (min, max)
// };
let styles = self.obj().style_context();
let (margin_top, margin_right, margin_bottom, margin_left) = {
let margin = styles.margin(gtk::StateFlags::NORMAL);
(margin.top as f64, margin.right as f64, margin.bottom as f64, margin.left as f64)
};
let width = self.obj().allocated_width() as f64 - margin_left - margin_right;
let height = self.obj().allocated_height() as f64 - margin_top - margin_bottom;
// let styles = self.obj().style_context();
// let (margin_top, margin_right, margin_bottom, margin_left) = {
// let margin = styles.margin(gtk4::StateFlags::NORMAL);
// (margin.top as f64, margin.right as f64, margin.bottom as f64, margin.left as f64)
// };
// let width = self.obj().allocated_width() as f64 - margin_left - margin_right;
// let height = self.obj().allocated_height() as f64 - margin_top - margin_bottom;
// Calculate graph points once
// Separating this into another function would require pasing a
// GraphPriv that would hide interior mutability
let points = {
let value_range = max - min;
let time_range = *self.time_range.borrow() as f64;
let last_updated_at = self.last_updated_at.borrow();
let mut points = history
.iter()
.map(|(instant, value)| {
let t = last_updated_at.duration_since(*instant).as_millis() as f64;
self.value_to_point(
width,
height,
t / time_range,
(value - min) / value_range,
)
})
.collect::<VecDeque<(f64, f64)>>();
// // Calculate graph points once
// // Separating this into another function would require pasing a
// // GraphPriv that would hide interior mutability
// let points = {
// let value_range = max - min;
// let time_range = *self.time_range.borrow() as f64;
// let last_updated_at = self.last_updated_at.borrow();
// let mut points = history
// .iter()
// .map(|(instant, value)| {
// let t = last_updated_at.duration_since(*instant).as_millis() as f64;
// self.value_to_point(
// width,
// height,
// t / time_range,
// (value - min) / value_range,
// )
// })
// .collect::<VecDeque<(f64, f64)>>();
// Aad an extra point outside of the graph to extend the line to the left
if let Some((instant, value)) = extra_point {
let t = last_updated_at.duration_since(instant).as_millis() as f64;
let (x, y) = self.value_to_point(
width,
height,
(t - time_range) / time_range,
(value - min) / value_range,
);
points.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) });
}
points
};
// // Aad an extra point outside of the graph to extend the line to the left
// if let Some((instant, value)) = extra_point {
// let t = last_updated_at.duration_since(instant).as_millis() as f64;
// let (x, y) = self.value_to_point(
// width,
// height,
// (t - time_range) / time_range,
// (value - min) / value_range,
// );
// points.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) });
// }
// points
// };
// Actually draw the graph
cr.save()?;
cr.translate(margin_left, margin_top);
cr.rectangle(0.0, 0.0, width, height);
cr.clip();
// // Actually draw the graph
// cr.save()?;
// cr.translate(margin_left, margin_top);
// cr.rectangle(0.0, 0.0, width, height);
// cr.clip();
// Draw Background
let bg_color: gdk::RGBA = styles
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
.get()?;
if bg_color.alpha() > 0.0 {
if let Some(first_point) = points.front() {
cr.line_to(first_point.0, height + margin_bottom);
}
for (x, y) in points.iter() {
cr.line_to(*x, *y);
}
cr.line_to(width, height);
// // Draw Background
// let bg_color: gdk::RGBA = styles
// .style_property_for_state("background-color", gtk4::StateFlags::NORMAL)
// .get()?;
// if bg_color.alpha() > 0.0 {
// if let Some(first_point) = points.front() {
// cr.line_to(first_point.0, height + margin_bottom);
// }
// for (x, y) in points.iter() {
// cr.line_to(*x, *y);
// }
// cr.line_to(width, height);
cr.set_source_rgba(
bg_color.red(),
bg_color.green(),
bg_color.blue(),
bg_color.alpha(),
);
cr.fill()?;
}
// cr.set_source_rgba(
// bg_color.red(),
// bg_color.green(),
// bg_color.blue(),
// bg_color.alpha(),
// );
// cr.fill()?;
// }
// Draw Line
let line_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL);
let thickness = *self.thickness.borrow();
if line_color.alpha() > 0.0 && thickness > 0.0 {
for (x, y) in points.iter() {
cr.line_to(*x, *y);
}
// // Draw Line
// let line_color: gdk::RGBA = styles.color(gtk4::StateFlags::NORMAL);
// let thickness = *self.thickness.borrow();
// if line_color.alpha() > 0.0 && thickness > 0.0 {
// for (x, y) in points.iter() {
// cr.line_to(*x, *y);
// }
let line_style = &*self.line_style.borrow();
apply_line_style(line_style.as_str(), cr)?;
cr.set_line_width(thickness);
cr.set_source_rgba(
line_color.red(),
line_color.green(),
line_color.blue(),
line_color.alpha(),
);
cr.stroke()?;
}
// let line_style = &*self.line_style.borrow();
// apply_line_style(line_style.as_str(), cr)?;
// cr.set_line_width(thickness);
// cr.set_source_rgba(
// line_color.red(),
// line_color.green(),
// line_color.blue(),
// line_color.alpha(),
// );
// cr.stroke()?;
// }
cr.reset_clip();
cr.restore()?;
Ok(())
})();
// cr.reset_clip();
// cr.restore()?;
// Ok(())
// })();
if let Err(error) = res {
error_handling_ctx::print_error(error)
};
// if let Err(error) = res {
// error_handling_ctx::print_error(error)
// };
glib::Propagation::Proceed
}
}
// glib::Propagation::Proceed
// }
// }
fn apply_line_style(style: &str, cr: &cairo::Context) -> Result<()> {
match style {
"miter" => {
cr.set_line_cap(cairo::LineCap::Butt);
cr.set_line_join(cairo::LineJoin::Miter);
}
"bevel" => {
cr.set_line_cap(cairo::LineCap::Square);
cr.set_line_join(cairo::LineJoin::Bevel);
}
"round" => {
cr.set_line_cap(cairo::LineCap::Round);
cr.set_line_join(cairo::LineJoin::Round);
}
_ => Err(anyhow!("Error, the value: {} for atribute join is not valid", style))?,
};
Ok(())
}
// fn apply_line_style(style: &str, cr: &cairo::Context) -> Result<()> {
// match style {
// "miter" => {
// cr.set_line_cap(cairo::LineCap::Butt);
// cr.set_line_join(cairo::LineJoin::Miter);
// }
// "bevel" => {
// cr.set_line_cap(cairo::LineCap::Square);
// cr.set_line_join(cairo::LineJoin::Bevel);
// }
// "round" => {
// cr.set_line_cap(cairo::LineCap::Round);
// cr.set_line_join(cairo::LineJoin::Round);
// }
// _ => Err(anyhow!("Error, the value: {} for atribute join is not valid", style))?,
// };
// Ok(())
// }

View File

@@ -4,4 +4,3 @@ pub mod graph;
pub mod transform;
pub mod widget_definitions;
pub mod widget_definitions_helper;
pub mod window;

View File

@@ -1,214 +1,214 @@
use crate::window::coords::NumWithUnit;
use anyhow::{anyhow, Result};
use gtk::glib::{self, object_subclass, wrapper, Properties};
use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, str::FromStr};
// use crate::window::coords::NumWithUnit;
// use anyhow::{anyhow, Result};
// use gtk4::glib::{self, object_subclass, wrapper, Properties};
// use gtk4::{prelude::*, subclass::prelude::*};
// use std::{cell::RefCell, str::FromStr};
use crate::error_handling_ctx;
// use crate::error_handling_ctx;
wrapper! {
pub struct Transform(ObjectSubclass<TransformPriv>)
@extends gtk::Bin, gtk::Container, gtk::Widget;
}
// wrapper! {
// pub struct Transform(ObjectSubclass<TransformPriv>)
// @extends gtk4::Bin, gtk4::Container, gtk4::Widget;
// }
#[derive(Properties)]
#[properties(wrapper_type = Transform)]
pub struct TransformPriv {
#[property(get, set, nick = "Rotate", blurb = "The Rotation", minimum = f64::MIN, maximum = f64::MAX, default = 0f64)]
rotate: RefCell<f64>,
// #[derive(Properties)]
// #[properties(wrapper_type = Transform)]
// pub struct TransformPriv {
// #[property(get, set, nick = "Rotate", blurb = "The Rotation", minimum = f64::MIN, maximum = f64::MAX, default = 0f64)]
// rotate: RefCell<f64>,
#[property(get, set, nick = "Transform-Origin X", blurb = "X coordinate (%/px) for the Transform-Origin", default = None)]
transform_origin_x: RefCell<Option<String>>,
// #[property(get, set, nick = "Transform-Origin X", blurb = "X coordinate (%/px) for the Transform-Origin", default = None)]
// transform_origin_x: RefCell<Option<String>>,
#[property(get, set, nick = "Transform-Origin Y", blurb = "Y coordinate (%/px) for the Transform-Origin", default = None)]
transform_origin_y: RefCell<Option<String>>,
// #[property(get, set, nick = "Transform-Origin Y", blurb = "Y coordinate (%/px) for the Transform-Origin", default = None)]
// transform_origin_y: RefCell<Option<String>>,
#[property(get, set, nick = "Translate x", blurb = "The X Translation", default = None)]
translate_x: RefCell<Option<String>>,
// #[property(get, set, nick = "Translate x", blurb = "The X Translation", default = None)]
// translate_x: RefCell<Option<String>>,
#[property(get, set, nick = "Translate y", blurb = "The Y Translation", default = None)]
translate_y: RefCell<Option<String>>,
// #[property(get, set, nick = "Translate y", blurb = "The Y Translation", default = None)]
// translate_y: RefCell<Option<String>>,
#[property(get, set, nick = "Scale x", blurb = "The amount to scale in x", default = None)]
scale_x: RefCell<Option<String>>,
// #[property(get, set, nick = "Scale x", blurb = "The amount to scale in x", default = None)]
// scale_x: RefCell<Option<String>>,
#[property(get, set, nick = "Scale y", blurb = "The amount to scale in y", default = None)]
scale_y: RefCell<Option<String>>,
// #[property(get, set, nick = "Scale y", blurb = "The amount to scale in y", default = None)]
// scale_y: RefCell<Option<String>>,
content: RefCell<Option<gtk::Widget>>,
}
// content: RefCell<Option<gtk4::Widget>>,
// }
// This should match the default values from the ParamSpecs
impl Default for TransformPriv {
fn default() -> Self {
TransformPriv {
rotate: RefCell::new(0.0),
transform_origin_x: RefCell::new(None),
transform_origin_y: RefCell::new(None),
translate_x: RefCell::new(None),
translate_y: RefCell::new(None),
scale_x: RefCell::new(None),
scale_y: RefCell::new(None),
content: RefCell::new(None),
}
}
}
// // This should match the default values from the ParamSpecs
// impl Default for TransformPriv {
// fn default() -> Self {
// TransformPriv {
// rotate: RefCell::new(0.0),
// transform_origin_x: RefCell::new(None),
// transform_origin_y: RefCell::new(None),
// translate_x: RefCell::new(None),
// translate_y: RefCell::new(None),
// scale_x: RefCell::new(None),
// scale_y: RefCell::new(None),
// content: RefCell::new(None),
// }
// }
// }
impl ObjectImpl for TransformPriv {
fn properties() -> &'static [glib::ParamSpec] {
Self::derived_properties()
}
// impl ObjectImpl for TransformPriv {
// fn properties() -> &'static [glib::ParamSpec] {
// Self::derived_properties()
// }
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"rotate" => {
self.rotate.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"transform-origin-x" => {
self.transform_origin_x.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"transform-origin-y" => {
self.transform_origin_y.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"translate-x" => {
self.translate_x.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"translate-y" => {
self.translate_y.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"scale-x" => {
self.scale_x.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
"scale-y" => {
self.scale_y.replace(value.get().unwrap());
self.obj().queue_draw(); // Queue a draw call with the updated value
}
x => panic!("Tried to set inexistant property of Transform: {}", x,),
}
}
// fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
// match pspec.name() {
// "rotate" => {
// self.rotate.replace(value.get().unwrap());
// self.obj().queue_draw(); // Queue a draw call with the updated value
// }
// "transform-origin-x" => {
// self.transform_origin_x.replace(value.get().unwrap());
// self.obj().queue_draw(); // Queue a draw call with the updated value
// }
// "transform-origin-y" => {
// self.transform_origin_y.replace(value.get().unwrap());
// self.obj().queue_draw(); // Queue a draw call with the updated value
// }
// "translate-x" => {
// self.translate_x.replace(value.get().unwrap());
// self.obj().queue_draw(); // Queue a draw call with the updated value
// }
// "translate-y" => {
// self.translate_y.replace(value.get().unwrap());
// self.obj().queue_draw(); // Queue a draw call with the updated value
// }
// "scale-x" => {
// self.scale_x.replace(value.get().unwrap());
// self.obj().queue_draw(); // Queue a draw call with the updated value
// }
// "scale-y" => {
// self.scale_y.replace(value.get().unwrap());
// self.obj().queue_draw(); // Queue a draw call with the updated value
// }
// x => panic!("Tried to set inexistant property of Transform: {}", x,),
// }
// }
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.derived_property(id, pspec)
}
}
// fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
// self.derived_property(id, pspec)
// }
// }
#[object_subclass]
impl ObjectSubclass for TransformPriv {
type ParentType = gtk::Bin;
type Type = Transform;
// #[object_subclass]
// impl ObjectSubclass for TransformPriv {
// type ParentType = gtk4::Bin;
// type Type = Transform;
const NAME: &'static str = "Transform";
// const NAME: &'static str = "Transform";
fn class_init(klass: &mut Self::Class) {
klass.set_css_name("transform");
}
}
// fn class_init(klass: &mut Self::Class) {
// klass.set_css_name("transform");
// }
// }
impl Default for Transform {
fn default() -> Self {
Self::new()
}
}
// impl Default for Transform {
// fn default() -> Self {
// Self::new()
// }
// }
impl Transform {
pub fn new() -> Self {
glib::Object::new::<Self>()
}
}
// impl Transform {
// pub fn new() -> Self {
// glib::Object::new::<Self>()
// }
// }
impl ContainerImpl for TransformPriv {
fn add(&self, widget: &gtk::Widget) {
if let Some(content) = &*self.content.borrow() {
// TODO: Handle this error when populating children widgets instead
error_handling_ctx::print_error(anyhow!(
"Error, trying to add multiple children to a circular-progress widget"
));
self.parent_remove(content);
}
self.parent_add(widget);
self.content.replace(Some(widget.clone()));
}
}
// impl ContainerImpl for TransformPriv {
// fn add(&self, widget: &gtk4::Widget) {
// if let Some(content) = &*self.content.borrow() {
// // TODO: Handle this error when populating children widgets instead
// error_handling_ctx::print_error(anyhow!(
// "Error, trying to add multiple children to a circular-progress widget"
// ));
// self.parent_remove(content);
// }
// self.parent_add(widget);
// self.content.replace(Some(widget.clone()));
// }
// }
impl BinImpl for TransformPriv {}
impl WidgetImpl for TransformPriv {
fn draw(&self, cr: &gtk::cairo::Context) -> glib::Propagation {
let res: Result<()> = (|| {
let rotate = *self.rotate.borrow();
let total_width = self.obj().allocated_width() as f64;
let total_height = self.obj().allocated_height() as f64;
// impl BinImpl for TransformPriv {}
// impl WidgetImpl for TransformPriv {
// fn draw(&self, cr: &gtk4::cairo::Context) -> glib::Propagation {
// let res: Result<()> = (|| {
// let rotate = *self.rotate.borrow();
// let total_width = self.obj().allocated_width() as f64;
// let total_height = self.obj().allocated_height() as f64;
cr.save()?;
// cr.save()?;
let transform_origin_x = match &*self.transform_origin_x.borrow() {
Some(rcx) => {
NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64
}
None => 0.0,
};
let transform_origin_y = match &*self.transform_origin_y.borrow() {
Some(rcy) => {
NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64
}
None => 0.0,
};
// let transform_origin_x = match &*self.transform_origin_x.borrow() {
// Some(rcx) => {
// NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64
// }
// None => 0.0,
// };
// let transform_origin_y = match &*self.transform_origin_y.borrow() {
// Some(rcy) => {
// NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64
// }
// None => 0.0,
// };
let translate_x = match &*self.translate_x.borrow() {
Some(tx) => {
NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64
}
None => 0.0,
};
// let translate_x = match &*self.translate_x.borrow() {
// Some(tx) => {
// NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64
// }
// None => 0.0,
// };
let translate_y = match &*self.translate_y.borrow() {
Some(ty) => {
NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64
}
None => 0.0,
};
// let translate_y = match &*self.translate_y.borrow() {
// Some(ty) => {
// NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64
// }
// None => 0.0,
// };
let scale_x = match &*self.scale_x.borrow() {
Some(sx) => {
NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0
}
None => 1.0,
};
// let scale_x = match &*self.scale_x.borrow() {
// Some(sx) => {
// NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0
// }
// None => 1.0,
// };
let scale_y = match &*self.scale_y.borrow() {
Some(sy) => {
NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0
}
None => 1.0,
};
// let scale_y = match &*self.scale_y.borrow() {
// Some(sy) => {
// NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0
// }
// None => 1.0,
// };
cr.translate(transform_origin_x, transform_origin_y);
cr.rotate(perc_to_rad(rotate));
cr.translate(translate_x - transform_origin_x, translate_y - transform_origin_y);
cr.scale(scale_x, scale_y);
// cr.translate(transform_origin_x, transform_origin_y);
// cr.rotate(perc_to_rad(rotate));
// cr.translate(translate_x - transform_origin_x, translate_y - transform_origin_y);
// cr.scale(scale_x, scale_y);
// Children widget
if let Some(child) = &*self.content.borrow() {
self.obj().propagate_draw(child, cr);
}
// // Children widget
// if let Some(child) = &*self.content.borrow() {
// self.obj().propagate_draw(child, cr);
// }
cr.restore()?;
Ok(())
})();
// cr.restore()?;
// Ok(())
// })();
if let Err(error) = res {
error_handling_ctx::print_error(error)
};
// if let Err(error) = res {
// error_handling_ctx::print_error(error)
// };
glib::Propagation::Proceed
}
}
// glib::Propagation::Proceed
// }
// }
fn perc_to_rad(n: f64) -> f64 {
(n / 100f64) * 2f64 * std::f64::consts::PI
}
// fn perc_to_rad(n: f64) -> f64 {
// (n / 100f64) * 2f64 * std::f64::consts::PI
// }

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,22 @@
use crate::gtk::prelude::LabelExt;
use anyhow::{anyhow, Result};
use gtk::pango;
use gtk4::glib;
use gtk4::glib::gobject_ffi;
use gtk4::glib::translate::{FromGlibPtrNone, IntoGlib};
use gtk4::glib::Value;
use gtk4::pango;
use gtk4::prelude::{Cast, ObjectExt, RangeExt, StaticType, ToValue};
use rhai::Map;
use std::process::Command;
// Run a command and get the output
pub(super) fn run_command<T>(
timeout: std::time::Duration,
cmd: &str,
args: &[T],
injected_vars: Option<Vec<(String, String)>>,
) where
pub(super) fn run_command<T>(timeout: std::time::Duration, cmd: &str, args: &[T])
where
T: 'static + std::fmt::Display + Send + Sync + Clone,
{
if cmd.is_empty() {
return;
}
use wait_timeout::ChildExt;
let cmd = replace_placeholders(cmd, args);
std::thread::Builder::new()
@@ -26,12 +30,6 @@ pub(super) fn run_command<T>(
let mut command = Command::new("/bin/sh");
command.arg("-c").arg(&cmd);
if let Some(vars) = injected_vars {
for (key, value) in vars {
command.env(key, value);
}
}
let child = command.spawn();
match child {
Ok(mut child) => match child.wait_timeout(timeout) {
@@ -85,29 +83,29 @@ pub fn dynamic_eq(a: &rhai::Dynamic, b: &rhai::Dynamic) -> bool {
}
/// ALL WIDGETS
pub(super) fn parse_align(o: &str) -> Result<gtk::Align> {
pub(super) fn parse_align(o: &str) -> Result<gtk4::Align> {
match o.to_ascii_lowercase().as_str() {
"fill" => Ok(gtk::Align::Fill),
"baseline" => Ok(gtk::Align::Baseline),
"center" => Ok(gtk::Align::Center),
"start" => Ok(gtk::Align::Start),
"end" => Ok(gtk::Align::End),
"fill" => Ok(gtk4::Align::Fill),
"baseline" => Ok(gtk4::Align::Baseline),
"center" => Ok(gtk4::Align::Center),
"start" => Ok(gtk4::Align::Start),
"end" => Ok(gtk4::Align::End),
other => Err(anyhow!("Invalid alignment: {}", other)),
}
}
/// Gtk Box
pub(super) fn parse_orientation(ori: &str) -> Result<gtk::Orientation> {
pub(super) fn parse_orientation(ori: &str) -> Result<gtk4::Orientation> {
match ori.to_ascii_lowercase().as_str() {
"h" | "horizontal" => Ok(gtk::Orientation::Horizontal),
"v" | "vertical" => Ok(gtk::Orientation::Vertical),
"h" | "horizontal" => Ok(gtk4::Orientation::Horizontal),
"v" | "vertical" => Ok(gtk4::Orientation::Vertical),
other => Err(anyhow!("Invalid orientation: {}", other)),
}
}
/// Gtk Label
pub(super) fn apply_ellipsize_settings(
label: &gtk::Label,
label: &gtk4::Label,
truncate: bool,
limit_width: i32,
truncate_left: bool,
@@ -136,12 +134,12 @@ pub(super) fn parse_gravity(s: &str) -> Result<pango::Gravity> {
}
}
pub(super) fn parse_justification(s: &str) -> Result<gtk::Justification> {
pub(super) fn parse_justification(s: &str) -> Result<gtk4::Justification> {
match s.to_ascii_lowercase().as_str() {
"left" => Ok(gtk::Justification::Left),
"right" => Ok(gtk::Justification::Right),
"center" => Ok(gtk::Justification::Center),
"fill" => Ok(gtk::Justification::Fill),
"left" => Ok(gtk4::Justification::Left),
"right" => Ok(gtk4::Justification::Right),
"center" => Ok(gtk4::Justification::Center),
"fill" => Ok(gtk4::Justification::Fill),
_ => Err(anyhow!("Invalid justification: '{}'", s)),
}
}
@@ -156,12 +154,23 @@ pub(super) fn parse_wrap_mode(s: &str) -> Result<pango::WrapMode> {
}
/// Gtk scale (slider)
pub(super) fn parse_position_type(s: &str) -> Result<gtk::PositionType> {
pub(super) fn parse_position_type(s: &str) -> Result<gtk4::PositionType> {
match s.to_ascii_lowercase().as_str() {
"left" => Ok(gtk::PositionType::Left),
"right" => Ok(gtk::PositionType::Right),
"top" => Ok(gtk::PositionType::Top),
"bottom" => Ok(gtk::PositionType::Bottom),
"left" => Ok(gtk4::PositionType::Left),
"right" => Ok(gtk4::PositionType::Right),
"top" => Ok(gtk4::PositionType::Top),
"bottom" => Ok(gtk4::PositionType::Bottom),
_ => Err(anyhow!("Invalid position type: '{}'", s)),
}
}
/// Gtk flow box
pub(super) fn parse_selection_model(s: &str) -> Result<gtk4::SelectionMode> {
match s.to_ascii_lowercase().as_str() {
"none" => Ok(gtk4::SelectionMode::None),
"single" => Ok(gtk4::SelectionMode::Single),
"browse" => Ok(gtk4::SelectionMode::Browse),
"multiple" => Ok(gtk4::SelectionMode::Multiple),
_ => Err(anyhow!("Invalid position type: '{}'", s)),
}
}
@@ -182,32 +191,18 @@ where
}
/// Revealer
pub(super) fn parse_revealer_transition(t: &str) -> Result<gtk::RevealerTransitionType> {
pub(super) fn parse_revealer_transition(t: &str) -> Result<gtk4::RevealerTransitionType> {
match t.to_ascii_lowercase().as_str() {
"slideright" => Ok(gtk::RevealerTransitionType::SlideRight),
"slideleft" => Ok(gtk::RevealerTransitionType::SlideLeft),
"slideup" => Ok(gtk::RevealerTransitionType::SlideUp),
"slidedown" => Ok(gtk::RevealerTransitionType::SlideDown),
"fade" | "crossfade" => Ok(gtk::RevealerTransitionType::Crossfade),
"none" => Ok(gtk::RevealerTransitionType::None),
"slideright" => Ok(gtk4::RevealerTransitionType::SlideRight),
"slideleft" => Ok(gtk4::RevealerTransitionType::SlideLeft),
"slideup" => Ok(gtk4::RevealerTransitionType::SlideUp),
"slidedown" => Ok(gtk4::RevealerTransitionType::SlideDown),
"fade" | "crossfade" => Ok(gtk4::RevealerTransitionType::Crossfade),
"none" => Ok(gtk4::RevealerTransitionType::None),
_ => Err(anyhow!("Invalid transition: '{}'", t)),
}
}
/// Gtk Image
// icon-size - "menu", "small-toolbar", "toolbar", "large-toolbar", "button", "dnd", "dialog"
pub(super) fn parse_icon_size(o: &str) -> Result<gtk::IconSize> {
match o.to_ascii_lowercase().as_str() {
"menu" => Ok(gtk::IconSize::Menu),
"small-toolbar" | "toolbar" => Ok(gtk::IconSize::SmallToolbar),
"large-toolbar" => Ok(gtk::IconSize::LargeToolbar),
"button" => Ok(gtk::IconSize::Button),
"dnd" => Ok(gtk::IconSize::Dnd),
"dialog" => Ok(gtk::IconSize::Dialog),
_ => Err(anyhow!("Invalid icon size: '{}'", o)),
}
}
/// Event box
// dragtype - "file", "text"
pub(super) enum DragEntryType {
@@ -225,14 +220,116 @@ pub(super) fn parse_dragtype(o: &str) -> Result<DragEntryType> {
/// Stack widget
// transition - "slideright", "slideleft", "slideup", "slidedown", "crossfade", "none"
pub(super) fn parse_stack_transition(t: &str) -> Result<gtk::StackTransitionType> {
pub(super) fn parse_stack_transition(t: &str) -> Result<gtk4::StackTransitionType> {
match t.to_ascii_lowercase().as_str() {
"slideright" => Ok(gtk::StackTransitionType::SlideRight),
"slideleft" => Ok(gtk::StackTransitionType::SlideLeft),
"slideup" => Ok(gtk::StackTransitionType::SlideUp),
"slidedown" => Ok(gtk::StackTransitionType::SlideDown),
"fade" | "crossfade" => Ok(gtk::StackTransitionType::Crossfade),
"none" => Ok(gtk::StackTransitionType::None),
"slideright" => Ok(gtk4::StackTransitionType::SlideRight),
"slideleft" => Ok(gtk4::StackTransitionType::SlideLeft),
"slideup" => Ok(gtk4::StackTransitionType::SlideUp),
"slidedown" => Ok(gtk4::StackTransitionType::SlideDown),
"fade" | "crossfade" => Ok(gtk4::StackTransitionType::Crossfade),
"none" => Ok(gtk4::StackTransitionType::None),
_ => Err(anyhow!("Invalid stack transition: '{}'", t)),
}
}
// For localbind
pub(super) fn set_property_from_string_anywhere(
widget: &gtk4::Widget,
prop_name: &str,
value_str: &str,
) {
fn convert(pspec: &glib::ParamSpec, value_str: &str) -> Option<Value> {
let value_type = pspec.value_type();
if value_type == f64::static_type() {
value_str.parse::<f64>().ok().map(|v| v.to_value())
} else if value_type == i32::static_type() {
value_str.parse::<i32>().ok().map(|v| v.to_value())
} else if value_type == bool::static_type() {
value_str.parse::<bool>().ok().map(|v| v.to_value())
} else if value_type == String::static_type() {
Some(value_str.to_value())
} else {
None
}
}
let obj: &glib::Object = widget.upcast_ref();
if let Some(pspec) = obj.find_property(prop_name) {
if let Some(gv) = convert(&pspec, value_str) {
obj.set_property(prop_name, &gv);
}
return;
}
unsafe {
for iface_type in obj.type_().interfaces() {
for pspec in list_interface_properties(iface_type) {
if pspec.name() == prop_name {
if let Some(v) = convert(&pspec, value_str) {
obj.set_property(prop_name, &v);
}
return;
}
}
}
}
if let Some(range) = widget.downcast_ref::<gtk4::Range>() {
let range_obj: &glib::Object = range.upcast_ref();
if let Some(pspec) = range_obj.find_property(prop_name) {
if let Some(gv) = convert(&pspec, value_str) {
range_obj.set_property(prop_name, &gv);
}
return;
}
let adj = range.adjustment();
let adj_obj: &glib::Object = adj.upcast_ref();
if let Some(pspec) = adj_obj.find_property(prop_name) {
if let Some(gv) = convert(&pspec, value_str) {
adj_obj.set_property(prop_name, &gv);
}
return;
}
}
log::error!("Property '{}' not found on widget {}", prop_name, obj.type_().name());
}
unsafe fn list_interface_properties(iface_type: glib::Type) -> Vec<glib::ParamSpec> {
let mut n_props = 0;
let iface_ptr = gobject_ffi::g_type_default_interface_ref(iface_type.into_glib());
if iface_ptr.is_null() {
return vec![];
}
let props_ptr =
gobject_ffi::g_object_interface_list_properties(iface_ptr as *mut _, &mut n_props);
let props = (0..n_props)
.map(|i| {
let p = *props_ptr.add(i as usize);
glib::ParamSpec::from_glib_none(p)
})
.collect::<Vec<_>>();
gobject_ffi::g_type_default_interface_unref(iface_ptr);
props
}
/// Picture widget
pub(super) fn parse_content_fit(cf: &str) -> Result<gtk4::ContentFit> {
match cf.to_ascii_lowercase().as_str() {
"fill" => Ok(gtk4::ContentFit::Fill),
"contain" => Ok(gtk4::ContentFit::Contain),
"cover" => Ok(gtk4::ContentFit::Cover),
"scaledown" => Ok(gtk4::ContentFit::ScaleDown),
_ => Err(anyhow!("Invalid content fit: '{}'", cf)),
}
}

View File

@@ -1,63 +0,0 @@
use gtk::glib::{self, object_subclass, wrapper, Properties};
use gtk::{prelude::*, subclass::prelude::*};
use std::cell::RefCell;
wrapper! {
pub struct Window(ObjectSubclass<WindowPriv>)
@extends gtk::Window, gtk::Bin, gtk::Container, gtk::Widget, @implements gtk::Buildable;
}
#[derive(Properties)]
#[properties(wrapper_type = Window)]
pub struct WindowPriv {
#[property(get, name = "x", nick = "X", blurb = "Global x coordinate", default = 0)]
x: RefCell<i32>,
#[property(get, name = "y", nick = "Y", blurb = "Global y coordinate", default = 0)]
y: RefCell<i32>,
}
// This should match the default values from the ParamSpecs
impl Default for WindowPriv {
fn default() -> Self {
WindowPriv { x: RefCell::new(0), y: RefCell::new(0) }
}
}
#[object_subclass]
impl ObjectSubclass for WindowPriv {
type ParentType = gtk::Window;
type Type = Window;
const NAME: &'static str = "WindowEwwii";
}
impl Default for Window {
fn default() -> Self {
glib::Object::new::<Self>()
}
}
impl Window {
pub fn new(type_: gtk::WindowType, x_: i32, y_: i32) -> Self {
let w: Self = glib::Object::builder().property("type", type_).build();
let priv_ = w.imp();
priv_.x.replace(x_);
priv_.y.replace(y_);
w
}
}
impl ObjectImpl for WindowPriv {
fn properties() -> &'static [glib::ParamSpec] {
Self::derived_properties()
}
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.derived_property(id, pspec)
}
}
impl WindowImpl for WindowPriv {}
impl BinImpl for WindowPriv {}
impl ContainerImpl for WindowPriv {}
impl WidgetImpl for WindowPriv {}

View File

@@ -81,6 +81,7 @@ impl BackendWindowOptionsDef {
exclusive: Self::get_optional(map, "exclusive")?,
focusable,
namespace: Self::get_optional(map, "namespace")?,
force_normal: Self::get_optional(map, "force_normal")?,
};
Ok(Self { wayland, x11 })
@@ -167,6 +168,7 @@ pub struct WlBackendWindowOptions {
pub exclusive: bool,
pub focusable: WlWindowFocusable,
pub namespace: Option<String>,
pub force_normal: bool,
}
/// Unevaluated form of [`WlBackendWindowOptions`]
@@ -175,6 +177,7 @@ pub struct WlBackendWindowOptionsDef {
pub exclusive: Option<bool>,
pub focusable: Option<String>,
pub namespace: Option<String>,
pub force_normal: Option<bool>,
}
impl WlBackendWindowOptionsDef {
@@ -189,6 +192,10 @@ impl WlBackendWindowOptionsDef {
None => WlWindowFocusable::default(),
},
namespace: properties.get("namespace").map(|d| d.clone_cast::<String>()),
force_normal: properties
.get("force_normal")
.map(|d| d.clone_cast::<bool>())
.unwrap_or(false),
})
}
}
@@ -219,9 +226,6 @@ impl FromStr for WlWindowFocusable {
"none" => Self::None,
"exclusive" => Self::Exclusive,
"ondemand" => Self::OnDemand,
// legacy support
"true" => Self::Exclusive,
"false" => Self::None,
}
}
}

View File

@@ -33,12 +33,13 @@ impl NumWithUnit {
}
}
pub fn perc_relative_to(&self, max: i32) -> f32 {
match *self {
NumWithUnit::Percent(n) => n,
NumWithUnit::Pixels(n) => ((n as f64 / max as f64) * 100.0) as f32,
}
}
// add back when needed
// pub fn perc_relative_to(&self, max: i32) -> f32 {
// match *self {
// NumWithUnit::Percent(n) => n,
// NumWithUnit::Pixels(n) => ((n as f64 / max as f64) * 100.0) as f32,
// }
// }
}
impl FromStr for NumWithUnit {

View File

@@ -0,0 +1,23 @@
[package]
name = "ewwii_plugin_api"
version = "0.7.0"
authors = ["byson94 <byson94wastaken@gmail.com>"]
edition = "2021"
license = "GPL-3.0-or-later"
description = "A shared library for building plugins for ewwii"
repository = "https://github.com/byson94/ewwii"
homepage = "https://github.com/byson94/ewwii"
[features]
default = ["include-gtk4", "include-rhai"]
## Include the gtk4 dependency
include-gtk4 = ["dep:gtk4"]
## Include the rhai dependency
include-rhai = ["dep:rhai"]
[dependencies]
# rhai crate features should exactly match that of rhai_impl
rhai = { workspace = true, optional = true, features = ["internals"] }
gtk4 = { workspace = true, optional = true }

View File

@@ -0,0 +1,28 @@
# ewwii_plugin_api
A shared interface providing traits for building plugins for ewwii.
## Example
A simple example showing how to use this interface is provided below:
```rust
use ewwii_plugin_api::{EwwiiAPI, Plugin, export_plugin};
pub struct DummyStructure;
impl Plugin for DummyStructure {
// critical for ewwii to launch the plugin
fn init(&self, host: &dyn EwwiiAPI) {
// will be printed by the host
host.log("Plugin says Hello!");
host.rhai_engine_action(Box::new(|engine| {
let ast = engine.compile("1+1");
println!("Compiled AST: {:#?}", ast);
}));
}
}
// Critical for ewwii to see the plugin
export_plugin!(DummyStructure);
```

View File

@@ -0,0 +1,11 @@
//! A module providing example implementations of plugins
/// An example plugin that can be exported directly
pub struct ExamplePlugin;
impl crate::Plugin for ExamplePlugin {
/// Example code that initalizes the plugin
fn init(&self, host: &dyn crate::EwwiiAPI) {
host.log("Example plugin says Hello!");
}
}

View File

@@ -0,0 +1,63 @@
//! Module implementing macros
/// Macro to implement and export a plugin in a single step.
/// With this macro, users can write their plugin code directly
/// without having to manually implement each trait.
///
/// ## Example
///
/// The following example shows how you can use this macro to
/// easily make plugins in a single step.
///
/// ```rust,ignore
/// use ewwii_plugin_api::auto_plugin;
///
/// auto_plugin!(MyPluginName, {
/// // host variable is passed in automatically
/// host.log("Easy, huh?");
/// });
/// ```
///
/// ## When not to use it
///
/// This macro shall not be used if you prefer flexibility and safety.
/// The manual approach is verbose, but is way safer and flexible than using this macro.
#[macro_export]
macro_rules! auto_plugin {
($struct_name:ident, $init_block:block) => {
pub struct $struct_name;
// Implement the Plugin trait
impl $crate::Plugin for $struct_name {
fn init(&self, host: &dyn $crate::EwwiiAPI) {
$init_block
}
}
$crate::export_plugin!($struct_name);
};
}
/// Automatically implements `create_plugin` for a given fieldless structure
#[macro_export]
macro_rules! export_plugin {
($plugin_struct:path) => {
#[unsafe(no_mangle)]
pub extern "C" fn create_plugin() -> Box<dyn $crate::Plugin> {
Box::new($plugin_struct)
}
};
}
/// Automatically implements `create_plugin` for a given structure that has fields.
///
/// This macro expects the structure to have fields and also implement a `default()` method.
#[macro_export]
macro_rules! export_stateful_plugin {
($plugin_struct:path) => {
#[unsafe(no_mangle)]
pub extern "C" fn create_plugin() -> Box<dyn $crate::Plugin> {
Box::new(<$plugin_struct>::default())
}
};
}

View File

@@ -0,0 +1,180 @@
//! # ewwii_plugin_api - A plugin interface for ewwii
//!
//! `ewwii_plguin_api` is a shared list of traits
//! that both ewwii and its plugins can use.
//! This crate simplifies and provides a safe way for building
//! plugins for ewwii.
//!
//! ## Example
//!
//! The following example shows how this crate shall be used to build ewwii plugins:
//!
//! ```rust
//! use ewwii_plugin_api::{EwwiiAPI, Plugin, export_plugin};
//!
//! pub struct DummyStructure;
//!
//! impl Plugin for DummyStructure {
//! // critical for ewwii to launch the plugin
//! fn init(&self, host: &dyn EwwiiAPI) {
//! // will be printed by the host
//! host.log("Plugin says Hello!");
//! }
//! }
//!
//! // Critical for ewwii to load the plugin
//! export_plugin!(DummyStructure);
//! ```
mod export_macros;
pub mod example;
pub mod rhai_backend;
pub mod widget_backend;
#[cfg(feature = "include-rhai")]
pub use rhai;
#[cfg(feature = "include-gtk4")]
pub use gtk4;
/// The shared trait defining the Ewwii plugin API
pub trait EwwiiAPI: Send + Sync {
// == General Stuff == //
/// Print a message from the host
fn print(&self, msg: &str);
/// Log a message from the host
fn log(&self, msg: &str);
/// Log a warning from the host
fn warn(&self, msg: &str);
/// Log an error from the host
fn error(&self, msg: &str);
// == Rhai Manipulation Stuff == //
/// _(include-rhai)_ Perform actions on the latest rhai engine.
///
/// # Example
///
/// ```rust
/// use ewwii_plugin_api::{EwwiiAPI, Plugin};
///
/// pub struct DummyStructure;
///
/// impl Plugin for DummyStructure {
/// fn init(&self, host: &dyn EwwiiAPI) {
/// host.rhai_engine_action(Box::new(|eng| {
/// // eng = rhai::Engine
/// eng.set_max_expr_depths(128, 128);
/// }));
/// }
/// }
/// ```
#[cfg(feature = "include-rhai")]
fn rhai_engine_action(
&self,
f: Box<dyn FnOnce(&mut rhai::Engine) + Send>,
) -> Result<(), String>;
/// _(include-rhai)_ Expose a function that rhai configuration can call.
///
/// **NOTE:***
///
/// Due to TypeID mismatches, methods like `register_type`, `register_fn`,
/// etc. won't work on the engine and may cause a crash. It is recommended
/// to use the `register_function` API to register a funtion which `api::slib`
/// can call to in rhai.
///
/// # Example
///
/// ```rust
/// use ewwii_plugin_api::{EwwiiAPI, Plugin, rhai_backend::RhaiFnNamespace};
/// use rhai::Dynamic;
///
/// pub struct DummyStructure;
///
/// impl Plugin for DummyStructure {
/// fn init(&self, host: &dyn EwwiiAPI) {
/// host.register_function(
/// "my_func".to_string(),
/// RhaiFnNamespace::Global,
/// Box::new(|args| {
/// // Do stuff
/// // - Perform things on the args (if needed)
/// // - And return a value
///
/// Ok(Dynamic::default()) // return empty
/// }));
/// }
/// }
/// ```
///
/// This example will register a function with signature "my_func(Array)" in rhai.
///
/// ## Example use in rhai
///
/// ```js
/// print(my_func(["param1", "param2"]));
/// ```
#[cfg(feature = "include-rhai")]
fn register_function(
&self,
name: String,
namespace: rhai_backend::RhaiFnNamespace,
f: Box<
dyn Fn(rhai::Array) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> + Send + Sync,
>,
) -> Result<(), String>;
// == Widget Rendering & Logic == //
/// Get the list of all widget id's
fn list_widget_ids(&self) -> Result<Vec<u64>, String>;
/// _(include-gtk4)_ Perform actions on the latest widget registry.
///
/// # Example
///
/// ```rust
/// use ewwii_plugin_api::{EwwiiAPI, Plugin};
///
/// pub struct DummyStructure;
///
/// impl Plugin for DummyStructure {
/// fn init(&self, host: &dyn EwwiiAPI) {
/// host.widget_reg_action(Box::new(|wrg| {
/// // wrg = widget_backend::WidgetRegistryRepr
/// // The gtk4::Widget can be modified here.
/// }));
/// }
/// }
/// ```
#[cfg(feature = "include-gtk4")]
fn widget_reg_action(
&self,
f: Box<dyn FnOnce(&mut widget_backend::WidgetRegistryRepr) + Send>,
) -> Result<(), String>;
}
/// The API format that the plugin should follow.
/// This trait should be implemented for a structure and
/// that structure should be exported via FFI.
///
/// ## Example
///
/// ```rust
/// use ewwii_plugin_api::{Plugin, EwwiiAPI, export_plugin};
///
/// struct MyStruct;
///
/// impl Plugin for MyStruct {
/// fn init(&self, host: &dyn EwwiiAPI) {
/// /* Implementation Skipped */
/// }
/// }
///
/// // Automatically does all the FFI related exports
/// export_plugin!(MyStruct);
/// ```
pub trait Plugin: Send + Sync {
/// Function ran by host to startup plugin (and its a must-have for plugin loading)
fn init(&self, host: &dyn EwwiiAPI);
}

View File

@@ -0,0 +1,14 @@
//! Module exposing extra utilities for rhai.
#[cfg(feature = "include-rhai")]
mod rhai_included {
/// _(include-rhai)_ An enumrate providing options for
/// function registaration namespaces.
pub enum RhaiFnNamespace {
Custom(String),
Global,
}
}
#[cfg(feature = "include-rhai")]
pub use rhai_included::*;

View File

@@ -0,0 +1,21 @@
//! Module exposing structures and types from the
//! Widget rendering and definition backend in ewwii.
#[cfg(feature = "include-gtk4")]
mod gtk4_included {
use gtk4::Widget as GtkWidget;
use std::collections::HashMap;
/// _(include-gtk4)_ A representation of widget registry which holds all the
/// information needed for the dynamic runtime engine in ewwii.
///
/// Not every change in this structure will be represented in the
/// original WidgetRegistry in ewwii. Only the change on gtk4::Widget
/// is reflected back.
pub struct WidgetRegistryRepr<'a> {
pub widgets: HashMap<u64, &'a mut GtkWidget>,
}
}
#[cfg(feature = "include-gtk4")]
pub use gtk4_included::*;

View File

@@ -10,10 +10,10 @@ homepage = "https://github.com/byson94/ewwii"
[dependencies]
shared_utils.workspace = true
scan_prop_proc.workspace = true
rhai = { workspace = true, features = ["internals"] }
anyhow.workspace = true
gtk.workspace = true
tokio = { workspace = true, features = ["full"] }
log.workspace = true
once_cell.workspace = true
@@ -22,5 +22,7 @@ ahash.workspace = true
nix = { workspace = true, features = ["process", "fs", "signal"] }
libc.workspace = true
# error handling
rhai_trace = "0.2.0"
codespan-reporting.workspace = true
rhai_trace = "0.3.1"
codespan-reporting.workspace = true
regex.workspace = true
gtk4.workspace = true

View File

@@ -1,20 +1,22 @@
use ahash::AHasher;
use anyhow::Result;
use rhai::Map;
use scan_prop_proc::scan_prop;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone)]
#[scan_prop]
pub enum WidgetNode {
Label { props: Map },
Box { props: Map, children: Vec<WidgetNode> },
CenterBox { props: Map, children: Vec<WidgetNode> },
FlowBox { props: Map, children: Vec<WidgetNode> },
Button { props: Map },
Image { props: Map },
Input { props: Map },
Progress { props: Map },
ComboBoxText { props: Map },
Slider { props: Map },
Scale { props: Map },
Checkbox { props: Map },
Expander { props: Map, children: Vec<WidgetNode> },
Revealer { props: Map, children: Vec<WidgetNode> },
@@ -30,6 +32,11 @@ pub enum WidgetNode {
EventBox { props: Map, children: Vec<WidgetNode> },
ToolTip { props: Map, children: Vec<WidgetNode> },
// Special
LocalBind { props: Map, children: Vec<WidgetNode> },
WidgetAction { props: Map, children: Vec<WidgetNode> },
GtkUI { props: Map },
// Top-level macros
DefWindow { name: String, props: Map, node: Box<WidgetNode> },
// Poll { var: String, interval: String, cmd: String, initial: String },
@@ -72,16 +79,9 @@ pub fn get_id_to_widget_info<'a>(
get_id_to_widget_info(child, id_to_props, Some(id))?;
}
}
WidgetNode::CenterBox { props, children } => {
let id = hash_props_and_type(props, "CenterBox");
insert_wdgt_info(
node,
props,
"CenterBox",
children.as_slice(),
parent_id,
id_to_props,
)?;
WidgetNode::FlowBox { props, children } => {
let id = hash_props_and_type(props, "FlowBox");
insert_wdgt_info(node, props, "FlowBox", children.as_slice(), parent_id, id_to_props)?;
for child in children {
get_id_to_widget_info(child, id_to_props, Some(id))?;
}
@@ -105,9 +105,9 @@ pub fn get_id_to_widget_info<'a>(
// let id = hash_props_and_type(props, "Transform");
insert_wdgt_info(node, props, "Transform", &[], parent_id, id_to_props)?;
}
WidgetNode::Slider { props } => {
// let id = hash_props_and_type(props, "Slider");
insert_wdgt_info(node, props, "Slider", &[], parent_id, id_to_props)?;
WidgetNode::Scale { props } => {
// let id = hash_props_and_type(props, "Scale");
insert_wdgt_info(node, props, "Scale", &[], parent_id, id_to_props)?;
}
WidgetNode::Progress { props } => {
// let id = hash_props_and_type(props, "Progress");
@@ -151,6 +151,37 @@ pub fn get_id_to_widget_info<'a>(
get_id_to_widget_info(child, id_to_props, Some(id))?;
}
}
WidgetNode::LocalBind { props, children } => {
let id = hash_props_and_type(props, "LocalBind");
insert_wdgt_info(
node,
props,
"LocalBind",
children.as_slice(),
parent_id,
id_to_props,
)?;
for child in children {
get_id_to_widget_info(child, id_to_props, Some(id))?;
}
}
WidgetNode::WidgetAction { props, children } => {
let id = hash_props_and_type(props, "WidgetAction");
insert_wdgt_info(
node,
props,
"WidgetAction",
children.as_slice(),
parent_id,
id_to_props,
)?;
for child in children {
get_id_to_widget_info(child, id_to_props, Some(id))?;
}
}
WidgetNode::GtkUI { props } => {
insert_wdgt_info(node, props, "GtkUI", &[], parent_id, id_to_props)?;
}
WidgetNode::ColorChooser { props } => {
// let id = hash_props_and_type(props, "ColorChooser");
insert_wdgt_info(node, props, "ColorChooser", &[], parent_id, id_to_props)?;
@@ -227,6 +258,14 @@ pub fn hash_props_and_type(props: &Map, widget_type_str: &str) -> u64 {
hasher.finish()
}
pub fn hash_props(props: &Map) -> u64 {
let mut hasher = AHasher::default();
props.hash(&mut hasher);
hasher.finish()
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,7 +1,9 @@
use crate::ast::WidgetNode;
use crate::ast::{hash_props, WidgetNode};
use crate::updates::{register_signal, LocalDataBinder, LocalSignal};
use rhai::{Array, Engine, EvalAltResult, Map, NativeCallContext};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
/// Converts a Dynamic array into a Vec<WidgetNode>, returning proper errors with position.
fn children_to_vec(
@@ -22,8 +24,13 @@ fn children_to_vec(
.collect()
}
pub fn register_all_widgets(engine: &mut Engine, all_nodes: &Rc<RefCell<Vec<WidgetNode>>>) {
pub fn register_all_widgets(
engine: &mut Engine,
all_nodes: &Rc<RefCell<Vec<WidgetNode>>>,
keep_signal: &Rc<RefCell<Vec<u64>>>,
) {
engine.register_type::<WidgetNode>();
engine.register_type::<LocalSignal>();
// == Primitive widgets ==
macro_rules! register_primitive {
@@ -40,7 +47,7 @@ pub fn register_all_widgets(engine: &mut Engine, all_nodes: &Rc<RefCell<Vec<Widg
register_primitive!("input", Input);
register_primitive!("progress", Progress);
register_primitive!("combo_box_text", ComboBoxText);
register_primitive!("slider", Slider);
register_primitive!("scale", Scale);
register_primitive!("checkbox", Checkbox);
register_primitive!("calendar", Calendar);
register_primitive!("graph", Graph);
@@ -66,7 +73,7 @@ pub fn register_all_widgets(engine: &mut Engine, all_nodes: &Rc<RefCell<Vec<Widg
}
register_with_children!("box", Box);
register_with_children!("centerbox", CenterBox);
register_with_children!("flowbox", FlowBox);
register_with_children!("expander", Expander);
register_with_children!("revealer", Revealer);
register_with_children!("scroll", Scroll);
@@ -74,6 +81,35 @@ pub fn register_all_widgets(engine: &mut Engine, all_nodes: &Rc<RefCell<Vec<Widg
register_with_children!("stack", Stack);
register_with_children!("eventbox", EventBox);
register_with_children!("tooltip", ToolTip);
register_with_children!("localbind", LocalBind);
register_with_children!("widget_action", WidgetAction);
// == Special widget
engine.register_fn(
"gtk_ui",
|path: &str, load: &str| -> Result<WidgetNode, Box<EvalAltResult>> {
let mut props = Map::new();
props.insert("file".into(), path.into());
props.insert("id".into(), load.into());
Ok(WidgetNode::GtkUI { props })
},
);
// == Special signal
let keep_signal_clone = keep_signal.clone();
engine.register_fn(
"localsignal",
move |props: Map| -> Result<LocalSignal, Box<EvalAltResult>> {
let id = hash_props(&props);
let signal = Rc::new(LocalSignal { id, props, data: Arc::new(LocalDataBinder::new()) });
let signal_rc = register_signal(id, signal);
keep_signal_clone.borrow_mut().push(id);
Ok((*signal_rc).clone())
},
);
// == Top-level macros ==
engine.register_fn(

View File

@@ -4,7 +4,7 @@ use rhai::{Dynamic, Map};
impl WidgetNode {
/// A very important implementation of [`WidgetNode`].
/// This function implements dyn_id property to widgets.
pub fn setup_for_rt(&self, parent_path: &str) -> Self {
pub fn setup_dyn_ids(&self, parent_path: &str) -> Self {
// fn to assign dyn_id to a node
fn with_dyn_id(mut props: Map, dyn_id: &str) -> Map {
props.insert("dyn_id".into(), Dynamic::from(dyn_id.to_string()));
@@ -22,7 +22,7 @@ impl WidgetNode {
.enumerate()
.map(|(idx, child)| {
let child_path = format!("{}_{}_{}", parent_path, kind, idx);
child.setup_for_rt(&child_path)
child.setup_dyn_ids(&child_path)
})
.collect()
}
@@ -31,7 +31,7 @@ impl WidgetNode {
WidgetNode::DefWindow { name, props, node } => WidgetNode::DefWindow {
name: name.clone(),
props: props.clone(),
node: Box::new(node.setup_for_rt(name)),
node: Box::new(node.setup_dyn_ids(name)),
},
// == Containers with children ==
@@ -39,9 +39,9 @@ impl WidgetNode {
props: with_dyn_id(props.clone(), parent_path),
children: process_children(children, parent_path, "box"),
},
WidgetNode::CenterBox { props, children } => WidgetNode::CenterBox {
WidgetNode::FlowBox { props, children } => WidgetNode::FlowBox {
props: with_dyn_id(props.clone(), parent_path),
children: process_children(children, parent_path, "centerbox"),
children: process_children(children, parent_path, "flowbox"),
},
WidgetNode::Expander { props, children } => WidgetNode::Expander {
props: with_dyn_id(props.clone(), parent_path),
@@ -71,6 +71,14 @@ impl WidgetNode {
props: with_dyn_id(props.clone(), parent_path),
children: process_children(children, parent_path, "tooltip"),
},
WidgetNode::LocalBind { props, children } => WidgetNode::LocalBind {
props: with_dyn_id(props.clone(), parent_path),
children: process_children(children, parent_path, "localbind"),
},
WidgetNode::WidgetAction { props, children } => WidgetNode::WidgetAction {
props: with_dyn_id(props.clone(), parent_path),
children: process_children(children, parent_path, "widget_action"),
},
// == Top-level container for multiple widgets ==
WidgetNode::Enter(children) => {
@@ -94,13 +102,14 @@ impl WidgetNode {
| node @ WidgetNode::Input { props }
| node @ WidgetNode::Progress { props }
| node @ WidgetNode::ComboBoxText { props }
| node @ WidgetNode::Slider { props }
| node @ WidgetNode::Scale { props }
| node @ WidgetNode::Checkbox { props }
| node @ WidgetNode::Calendar { props }
| node @ WidgetNode::ColorButton { props }
| node @ WidgetNode::ColorChooser { props }
| node @ WidgetNode::CircularProgress { props }
| node @ WidgetNode::Graph { props }
| node @ WidgetNode::GtkUI { props }
| node @ WidgetNode::Transform { props } => {
let new_props = with_dyn_id(props.clone(), parent_path);
match node {
@@ -112,7 +121,7 @@ impl WidgetNode {
WidgetNode::ComboBoxText { .. } => {
WidgetNode::ComboBoxText { props: new_props }
}
WidgetNode::Slider { .. } => WidgetNode::Slider { props: new_props },
WidgetNode::Scale { .. } => WidgetNode::Scale { props: new_props },
WidgetNode::Checkbox { .. } => WidgetNode::Checkbox { props: new_props },
WidgetNode::Calendar { .. } => WidgetNode::Calendar { props: new_props },
WidgetNode::ColorButton { .. } => WidgetNode::ColorButton { props: new_props },
@@ -123,6 +132,7 @@ impl WidgetNode {
WidgetNode::CircularProgress { props: new_props }
}
WidgetNode::Graph { .. } => WidgetNode::Graph { props: new_props },
WidgetNode::GtkUI { .. } => WidgetNode::GtkUI { props: new_props },
WidgetNode::Transform { .. } => WidgetNode::Transform { props: new_props },
_ => unreachable!(),
}

View File

@@ -5,34 +5,51 @@ use rhai::{Engine, EvalAltResult, ParseError};
use rhai_trace::{BetterError, Span};
/// Return a formatted Rhai evaluation error.
pub fn format_eval_error(error: &EvalAltResult, code: &str, engine: &Engine) -> String {
pub fn format_eval_error(
error: &EvalAltResult,
code: &str,
engine: &Engine,
file_id: Option<&str>,
) -> String {
let error_str = error.to_string();
if error_str == "" || error_str == "module_eval_failed" || error_str == "module_parse_failed" {
return String::new();
}
let better_error =
BetterError::improve_eval_error(error, code, engine, None).unwrap_or(BetterError {
message: error.to_string(),
message: error_str,
help: None,
hint: None,
note: None,
span: Span::new(0, 0, 0, 0),
});
format_codespan_error(better_error, code)
format_codespan_error(better_error, code, file_id)
}
/// Return a formatted Rhai parse error.
pub fn format_parse_error(error: &ParseError, code: &str) -> String {
pub fn format_parse_error(error: &ParseError, code: &str, file_id: Option<&str>) -> String {
let error_str = error.to_string();
if error_str == "" || error_str == "module_eval_failed" || error_str == "module_parse_failed" {
return String::new();
}
let better_error = BetterError::improve_parse_error(error, code).unwrap_or(BetterError {
message: error.to_string(),
message: error_str,
help: None,
hint: None,
note: None,
span: Span::new(0, 0, 0, 0),
});
format_codespan_error(better_error, code)
format_codespan_error(better_error, code, file_id)
}
/// Return a formatted error as a String
pub fn format_codespan_error(be: BetterError, code: &str) -> String {
pub fn format_codespan_error(be: BetterError, code: &str, file_id: Option<&str>) -> String {
let mut files = SimpleFiles::new();
let file_id = files.add("<rhai>", code);
let file_id = files.add(file_id.unwrap_or("<rhai>"), code);
// build the notes
let mut notes = Vec::new();
@@ -47,12 +64,15 @@ pub fn format_codespan_error(be: BetterError, code: &str) -> String {
}
// build the diagnostic error
let diagnostic = Diagnostic::error()
.with_message(&be.message)
.with_labels(vec![
Label::primary(file_id, be.span.start()..be.span.end()).with_message(&be.message)
])
.with_notes(notes);
let mut labels = Vec::new();
if be.span.start() != be.span.end() {
labels.push(
Label::primary(file_id, be.span.start()..be.span.end()).with_message(&be.message),
);
}
let diagnostic =
Diagnostic::error().with_message(&be.message).with_labels(labels).with_notes(notes);
let mut buffer = Buffer::ansi();
let config = term::Config::default();

View File

@@ -1,13 +1,44 @@
use crate::error::format_eval_error;
use anyhow::Result;
use rhai::Engine;
use std::{
collections::HashSet,
fs,
path::{Path, PathBuf},
};
pub fn extract_poll_and_listen_vars(code: &str) -> Result<Vec<(String, Option<String>)>> {
extract_poll_and_listen_vars_inner(code, &mut HashSet::new())
}
fn extract_poll_and_listen_vars_inner(
code: &str,
visited: &mut HashSet<PathBuf>,
) -> Result<Vec<(String, Option<String>)>> {
let mut results = Vec::new();
let mut engine = Engine::new();
register_temp_poll_listen(&mut engine);
// Handle imports manually
for import_path in extract_import_paths(code)? {
let resolved = resolve_import_path(&import_path)?;
// Prevent infinite recursion
let canonical = fs::canonicalize(&resolved).unwrap_or(resolved.clone());
if visited.contains(&canonical) {
continue;
}
visited.insert(canonical.clone());
if resolved.exists() {
let imported_code = fs::read_to_string(&resolved)?;
let inner = extract_poll_and_listen_vars_inner(&imported_code, visited)?;
results.extend(inner);
}
}
// Process this files own poll/listen calls
for expr in extract_poll_listen_exprs(code) {
match engine.eval_expression::<TempSignal>(&expr) {
Ok(sig) => {
@@ -15,7 +46,7 @@ pub fn extract_poll_and_listen_vars(code: &str) -> Result<Vec<(String, Option<St
results.push((sig.var, initial));
}
Err(e) => {
return Err(anyhow::anyhow!(format_eval_error(&e, code, &engine)));
return Err(anyhow::anyhow!(format_eval_error(&e, code, &engine, None)));
}
}
}
@@ -23,6 +54,38 @@ pub fn extract_poll_and_listen_vars(code: &str) -> Result<Vec<(String, Option<St
Ok(results)
}
/// Extract import paths from the Rhai source code
fn extract_import_paths(code: &str) -> Result<Vec<String>> {
let mut imports = Vec::new();
for line in code.lines() {
let trimmed = line.trim_start();
if trimmed.starts_with("import ") {
if let Some(start) = trimmed.find('"') {
if let Some(end_rel) = trimmed[start + 1..].find('"') {
let end = start + 1 + end_rel;
let path = &trimmed[start + 1..end];
imports.push(path.to_string());
}
}
}
}
Ok(imports)
}
/// Resolve relative and absolute import paths.
fn resolve_import_path(import_path: &str) -> Result<PathBuf> {
let path = Path::new(import_path);
let abs =
if path.is_absolute() { path.to_path_buf() } else { std::env::current_dir()?.join(path) };
let abs = if abs.extension().is_none() { abs.with_extension("rhai") } else { abs };
Ok(abs)
}
pub fn extract_poll_listen_exprs(code: &str) -> Vec<String> {
let mut exprs = Vec::new();
let mut i = 0;
@@ -35,7 +98,7 @@ pub fn extract_poll_listen_exprs(code: &str) -> Vec<String> {
while i < len && code_bytes[i] as char != '\n' {
i += 1;
}
i += 1; // skipp a full line
i += 1; // skip a full line
continue;
}

View File

@@ -1,4 +1,4 @@
//! IIRhai is a simple crate which configures rhai for the `ewwii` widget system.
//! rhai_impl is a simple crate which configures rhai for the `ewwii` widget system.
//!
//! This crate supports parsing, error handling, and has a custom module_resolver.

View File

@@ -1,11 +1,16 @@
use crate::error::{format_eval_error, format_parse_error};
use crate::parser::ParseConfig;
use rhai::{Engine, EvalAltResult, Module, ModuleResolver, Position, AST};
use crate::updates::ReactiveVarStore;
use rhai::Scope;
use rhai::{Dynamic, Engine, EvalAltResult, Module, ModuleResolver, Position, AST};
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::rc::Rc;
pub struct SimpleFileResolver;
pub struct SimpleFileResolver {
pub pl_handler_store: Option<ReactiveVarStore>,
}
impl ModuleResolver for SimpleFileResolver {
fn resolve(
@@ -45,18 +50,51 @@ impl ModuleResolver for SimpleFileResolver {
})?;
let ast: AST = engine.compile(&script).map_err(|e| {
log::error!("{}", format_parse_error(&e, &script));
e
})?;
let scope = ParseConfig::initial_poll_listen_scope(&script).map_err(|e| {
EvalAltResult::ErrorSystem(
format!("error setting up default variables: {full_path:?}"),
e.into(),
)
Box::new(EvalAltResult::ErrorSystem(
"module_parse_failed".into(),
format_parse_error(&e, &script, full_path.to_str()).into(),
))
})?;
let parent_script: Option<String> = if let Some(parent_path) = source_path {
match fs::read_to_string(parent_path) {
Ok(s) => Some(s),
Err(err) => {
log::error!("Could not read parent script {parent_path:?}: {err}");
None
}
}
} else {
None
};
let mut scope = if let Some(ref script) = parent_script {
ParseConfig::initial_poll_listen_scope(script).map_err(|e| {
EvalAltResult::ErrorSystem(
format!("error setting up default variables from {source_path:?}"),
e.into(),
)
})?
} else {
Scope::new()
};
match &self.pl_handler_store {
Some(val) => {
let name_to_val: &HashMap<String, String> = &*val.read().unwrap();
for (name, val) in name_to_val {
scope.set_value(name.clone(), Dynamic::from(val.clone()));
}
}
None => {}
}
let mut module = Module::eval_ast_as_new(scope, &ast, engine).map_err(|e| {
log::error!("{}", format_eval_error(&e, &script, engine));
e
Box::new(EvalAltResult::ErrorSystem(
"module_eval_failed".into(),
format_eval_error(&e, &script, engine, full_path.to_str()).into(),
))
})?;
module.build_index();
@@ -80,6 +118,12 @@ impl<R1: ModuleResolver, R2: ModuleResolver> ModuleResolver for ChainedResolver<
match self.first.resolve(engine, source_path, path, pos) {
Ok(m) => Ok(m),
Err(e1) => {
if let EvalAltResult::ErrorSystem(msg, _) = e1.as_ref() {
if msg == "module_eval_failed" || msg == "module_parse_failed" {
return Err(e1);
}
}
log::trace!(
"Error executing resolver 1, falling back to resolver 2. Error details: {}",
e1

View File

@@ -5,40 +5,45 @@ use crate::{
helper::extract_poll_and_listen_vars,
module_resolver::SimpleFileResolver,
providers::register_all_providers,
updates::ReactiveVarStore,
};
use anyhow::{anyhow, Result};
use rhai::{Dynamic, Engine, Scope, AST};
use rhai::{Dynamic, Engine, ImmutableString, OptimizationLevel, Scope, AST};
use std::cell::RefCell;
use std::fs;
use std::path::Path;
use std::rc::Rc;
pub struct ParseConfig {
engine: Engine,
pub engine: Engine,
all_nodes: Rc<RefCell<Vec<WidgetNode>>>,
keep_signal: Rc<RefCell<Vec<u64>>>,
}
impl ParseConfig {
pub fn new() -> Self {
pub fn new(pl_handler_store: Option<ReactiveVarStore>) -> Self {
let mut engine = Engine::new();
let all_nodes = Rc::new(RefCell::new(Vec::new()));
let keep_signal = Rc::new(RefCell::new(Vec::new()));
engine.set_max_expr_depths(128, 128);
engine.set_module_resolver(SimpleFileResolver);
engine
.set_module_resolver(SimpleFileResolver { pl_handler_store: pl_handler_store.clone() });
register_all_widgets(&mut engine, &all_nodes);
register_all_providers(&mut engine);
register_all_widgets(&mut engine, &all_nodes, &keep_signal);
register_all_providers(&mut engine, pl_handler_store);
Self { engine, all_nodes }
Self { engine, all_nodes, keep_signal }
}
pub fn eval_code(&mut self, code: &str) -> Result<WidgetNode> {
let ast = self.engine.compile(code)?;
self.eval_code_with(code, None, Some(&ast))
}
pub fn compile_code(&mut self, code: &str, file_path: &str) -> Result<AST> {
let mut ast = self
.engine
.compile(code)
.map_err(|e| anyhow!(format_parse_error(&e, code, Some(file_path))))?;
pub fn compile_code(&mut self, code: &str) -> Result<AST> {
self.engine.compile(code).map_err(|e| anyhow!(format_parse_error(&e, code)))
ast.set_source(ImmutableString::from(file_path));
Ok(ast)
}
pub fn eval_code_with(
@@ -46,6 +51,7 @@ impl ParseConfig {
code: &str,
rhai_scope: Option<Scope>,
compiled_ast: Option<&AST>,
file_id: Option<&str>,
) -> Result<WidgetNode> {
let mut scope = match rhai_scope {
Some(s) => s,
@@ -57,14 +63,17 @@ impl ParseConfig {
let _ = self
.engine
.eval_ast_with_scope::<Dynamic>(&mut scope, &ast)
.map_err(|e| anyhow!(format_eval_error(&e, code, &self.engine)))?;
.map_err(|e| anyhow!(format_eval_error(&e, code, &self.engine, file_id)))?;
} else {
let _ = self
.engine
.eval_with_scope::<Dynamic>(&mut scope, code)
.map_err(|e| anyhow!(format_eval_error(&e, code, &self.engine)))?;
.map_err(|e| anyhow!(format_eval_error(&e, code, &self.engine, file_id)))?;
};
// Retain signals
crate::updates::retain_signals(&self.keep_signal.borrow());
// Merge all nodes in all_nodes (`enter([])`) into a single root node
let merged_node = {
let mut all_nodes_vec = self.all_nodes.borrow_mut();
@@ -83,7 +92,25 @@ impl ParseConfig {
WidgetNode::Enter(merged_children)
};
Ok(merged_node.setup_for_rt("root"))
Ok(merged_node.setup_dyn_ids("root"))
}
pub fn eval_code_snippet(&mut self, code: &str) -> Result<WidgetNode> {
let mut scope = Scope::new();
// Just eval as node will be in `all_nodes`
let node = self
.engine
.eval_with_scope::<WidgetNode>(&mut scope, code)
.map_err(|e| anyhow!(format_eval_error(&e, code, &self.engine, Some("<dyn eval>"))))?;
// Retain signals
crate::updates::retain_signals(&self.keep_signal.borrow());
// Clear all nodes
self.all_nodes.borrow_mut().clear();
Ok(node)
}
pub fn code_from_file<P: AsRef<Path>>(&mut self, file_path: P) -> Result<String> {
@@ -144,4 +171,15 @@ impl ParseConfig {
}
}
}
pub fn set_opt_level(&mut self, opt_lvl: OptimizationLevel) {
self.engine.set_optimization_level(opt_lvl);
}
pub fn action_with_engine<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Engine) -> R,
{
f(&mut self.engine)
}
}

View File

@@ -20,7 +20,7 @@ pub mod linux {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::linux" as linux;
///
/// let k_version = linux::get_kernel_version();
@@ -44,7 +44,7 @@ pub mod linux {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::linux" as linux;
///
/// let battery_perc = linux::get_battery_perc();
@@ -88,7 +88,7 @@ pub mod linux {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::linux" as linux;
///
/// let cpu_info = linux::get_cpu_info();
@@ -143,7 +143,7 @@ pub mod linux {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::linux" as linux;
///
/// let ram_info = linux::get_ram_info();
@@ -201,7 +201,7 @@ pub mod linux {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::linux" as linux;
///
/// let gpu_info = linux::get_gpu_info();
@@ -272,7 +272,7 @@ pub mod linux {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::linux" as linux;
///
/// let disk_info = linux::get_disk_info();

View File

@@ -21,7 +21,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// let networks = wifi::scan();
@@ -66,7 +66,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// let networks = wifi::scan();
@@ -111,7 +111,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// let networks = wifi::scan();
@@ -146,7 +146,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// let connection = wifi::current_connection();
@@ -225,7 +225,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// wifi::connect("MySecretNetwork", "password123");
@@ -277,7 +277,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// wifi::connect_without_password("MySecretNetwork", "password123");
@@ -329,7 +329,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// wifi::disconnect();
@@ -402,7 +402,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// wifi::disable_adapter();
@@ -453,7 +453,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// wifi::enable_adapter();
@@ -516,7 +516,7 @@ pub mod wifi {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "api::wifi" as wifi;
///
/// wifi::enable_adapter();

View File

@@ -11,19 +11,23 @@ mod apilib;
mod stdlib;
use crate::module_resolver::{ChainedResolver, SimpleFileResolver};
use crate::updates::ReactiveVarStore;
use rhai::module_resolvers::StaticModuleResolver;
// expose the api's publically
pub use apilib::register_apilib;
pub use stdlib::register_stdlib;
pub fn register_all_providers(engine: &mut rhai::Engine) {
pub fn register_all_providers(engine: &mut rhai::Engine, plhs: Option<ReactiveVarStore>) {
let mut resolver = StaticModuleResolver::new();
// modules
register_stdlib(&mut resolver);
register_apilib(&mut resolver);
let chained = ChainedResolver { first: SimpleFileResolver, second: resolver.clone() };
let chained = ChainedResolver {
first: SimpleFileResolver { pl_handler_store: plhs.clone() },
second: resolver.clone(),
};
engine.set_module_resolver(chained);
}

View File

@@ -17,7 +17,7 @@ pub mod command {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::command" as cmd;
///
/// // Run a shell command (e.g., list directory contents)
@@ -46,7 +46,7 @@ pub mod command {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::command" as cmd;
///
/// // Run a shell command and capture its output

View File

@@ -16,7 +16,7 @@ pub mod env {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::env" as env;
///
/// // Get the value of the "HOME" environment variable
@@ -41,7 +41,7 @@ pub mod env {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::env" as env;
///
/// // Set the value of the "MY_VAR" environment variable
@@ -60,7 +60,7 @@ pub mod env {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::env" as env;
///
/// // Get the home directory
@@ -81,7 +81,7 @@ pub mod env {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::env" as env;
///
/// // Get the current working directory
@@ -106,7 +106,7 @@ pub mod env {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::env" as env;
///
/// // Get the username of the current user

View File

@@ -1,23 +1,23 @@
pub mod command;
pub mod env;
pub mod monitor;
pub mod regex;
pub mod text;
use rhai::exported_module;
use rhai::module_resolvers::StaticModuleResolver;
pub fn register_stdlib(resolver: &mut StaticModuleResolver) {
use crate::providers::stdlib::{command::command, env::env, monitor::monitor, text::text};
use crate::providers::stdlib::{command::command, env::env, regex::regex_lib, text::text};
// adding modules
let text_mod = exported_module!(text);
let env_mod = exported_module!(env);
let monitor_mod = exported_module!(monitor);
let command_mod = exported_module!(command);
let regex_mod = exported_module!(regex_lib);
// inserting modules
resolver.insert("std::text", text_mod);
resolver.insert("std::env", env_mod);
resolver.insert("std::monitor", monitor_mod);
resolver.insert("std::command", command_mod);
resolver.insert("std::regex", regex_mod);
}

View File

@@ -1,249 +0,0 @@
use gtk::gdk;
use gtk::prelude::*;
use rhai::plugin::*;
#[export_module]
pub mod monitor {
/// Get the number of connected monitors.
///
/// # Returns
///
/// Returns the total number of connected monitors as an `i64`.
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let count = monitor::count();
/// print(count); // Output: Number of connected monitors
/// ```
pub fn count() -> i64 {
get_monitor_count()
}
/// Get the resolution of the primary monitor.
///
/// # Returns
///
/// Returns an array containing the width and height of the primary monitor as two `i64` values.
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let resolution = monitor::primary_resolution();
/// print(resolution); // Output: [width, height]
/// ```
pub fn primary_resolution() -> [i64; 2] {
let (w, h) = get_primary_monitor_resolution();
[w, h]
}
/// Get the resolution of the primary monitor as a string.
///
/// # Returns
///
/// Returns the resolution of the primary monitor as a string in the format "width x height".
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let resolution_str = monitor::primary_resolution_str();
/// print(resolution_str); // Output: "1920x1080"
/// ```
pub fn primary_resolution_str() -> String {
let (w, h) = get_primary_monitor_resolution();
format!("{w}x{h}")
}
/// Get the resolutions of all connected monitors.
///
/// # Returns
///
/// Returns an array of arrays, where each inner array contains the width and height of a monitor.
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let resolutions = monitor::all_resolutions();
/// print(resolutions); // Output: [[width1, height1], [width2, height2], ...]
/// ```
pub fn all_resolutions() -> Vec<[i64; 2]> {
get_all_monitor_resolutions().into_iter().map(|(w, h)| [w, h]).collect()
}
/// Get the resolutions of all connected monitors as a string.
///
/// # Returns
///
/// Returns a string where each monitor's resolution is formatted as "width x height", separated by commas.
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let resolutions_str = monitor::all_resolutions_str();
/// print(resolutions_str); // Output: "1920x1080, 1280x720"
/// ```
pub fn all_resolutions_str() -> String {
get_all_monitor_resolutions()
.into_iter()
.map(|(w, h)| format!("{w}x{h}"))
.collect::<Vec<_>>()
.join(", ")
}
/// Get the dimensions (x, y, width, height) of a specific monitor.
///
/// # Arguments
///
/// * `index` - The index of the monitor (0-based).
///
/// # Returns
///
/// Returns an array with the monitor's position (x, y) and size (width, height).
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let dimensions = monitor::dimensions(0);
/// print(dimensions); // Output: [x, y, width, height]
/// ```
pub fn dimensions(index: i64) -> [i64; 4] {
let (x, y, w, h) = get_monitor_dimensions(index as usize);
[x, y, w, h]
}
/// Get the dimensions of a specific monitor as a string.
///
/// # Arguments
///
/// * `index` - The index of the monitor (0-based).
///
/// # Returns
///
/// Returns the monitor's dimensions as a string in the format "x,y - width x height".
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let dimensions_str = monitor::dimensions_str(0);
/// print(dimensions_str); // Output: "0,0 - 1920x1080"
/// ```
pub fn dimensions_str(index: i64) -> String {
let (x, y, w, h) = get_monitor_dimensions(index as usize);
format!("{x},{y} - {w}x{h}")
}
/// Get the DPI (dots per inch) of a specific monitor.
///
/// # Arguments
///
/// * `index` - The index of the monitor (0-based).
///
/// # Returns
///
/// Returns the DPI (scale factor * base DPI) of the monitor as a `f64`.
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let dpi = monitor::dpi(0);
/// print(dpi); // Output: DPI of the monitor
/// ```
pub fn dpi(index: i64) -> f64 {
get_monitor_dpi(index as usize)
}
/// Get the DPI of a specific monitor as a string.
///
/// # Arguments
///
/// * `index` - The index of the monitor (0-based).
///
/// # Returns
///
/// Returns the DPI of the monitor as a string formatted to 1 decimal place.
///
/// # Example
///
/// ```js
/// import "std::monitor" as monitor;
///
/// let dpi_str = monitor::dpi_str(0);
/// print(dpi_str); // Output: "96.0"
/// ```
pub fn dpi_str(index: i64) -> String {
format!("{:.1}", get_monitor_dpi(index as usize))
}
}
fn ensure_gdk_init() {
if gtk::is_initialized_main_thread() {
return;
}
gtk::init().expect("Failed to initialize GTK");
}
fn get_monitor_count() -> i64 {
ensure_gdk_init();
let display = gdk::Display::default().expect("No display found");
display.n_monitors() as i64
}
fn get_primary_monitor_resolution() -> (i64, i64) {
ensure_gdk_init();
let display = gdk::Display::default().expect("No display found");
if let Some(primary) = display.primary_monitor() {
let rect = primary.geometry();
(rect.width() as i64, rect.height() as i64)
} else {
(0, 0)
}
}
fn get_all_monitor_resolutions() -> Vec<(i64, i64)> {
ensure_gdk_init();
let display = gdk::Display::default().expect("No display found");
(0..display.n_monitors())
.filter_map(|i| display.monitor(i))
.map(|m| {
let rect = m.geometry();
(rect.width() as i64, rect.height() as i64)
})
.collect()
}
fn get_monitor_dimensions(index: usize) -> (i64, i64, i64, i64) {
ensure_gdk_init();
let display = gdk::Display::default().expect("No display found");
if let Some(m) = display.monitor(index as i32) {
let geom = m.geometry();
(geom.x() as i64, geom.y() as i64, geom.width() as i64, geom.height() as i64)
} else {
(0, 0, 0, 0)
}
}
fn get_monitor_dpi(index: usize) -> f64 {
ensure_gdk_init();
let display = gdk::Display::default().expect("No display found");
if let Some(m) = display.monitor(index as i32) {
m.scale_factor() as f64 * 96.0 // base DPI * scale factor
} else {
0.0
}
}

View File

@@ -0,0 +1,120 @@
use regex::Regex;
use rhai::plugin::*;
use rhai::{Array, Dynamic};
/// A module providing regular expression support.
#[export_module]
pub mod regex_lib {
/// Checks if a regex pattern matches the given text.
///
/// # Arguments
///
/// * `text` - A string to be matched with the pattern.
/// * `pattern` - The pattern to match on the string.
///
/// # Returns
///
/// Returns a boolean (true/false) based on if the pattern is matched on the text provided.
///
/// # Example
///
/// ```javascript
/// import "std::regex" as regex;
///
/// let result = regex::is_match("This is an example!", "example");
/// if result == true {
/// print("The pattern is matched!");
/// }
/// ```
#[rhai_fn(return_raw)]
pub fn is_match(text: &str, pattern: &str) -> Result<bool, Box<EvalAltResult>> {
let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?;
Ok(re.is_match(text))
}
/// Returns the first match of a regex pattern in the text.
///
/// # Arguments
///
/// * `text` - A string to be matched with the pattern.
/// * `pattern` - The pattern to match on the string.
///
/// # Returns
///
/// Returns a string which is the first match of a regex pattern.
///
/// # Example
///
/// ```javascript
/// import "std::regex" as regex;
///
/// let result = regex::find("This is an example!", `\be\w*\b`);
/// print(result); // output: "example"
/// ```
#[rhai_fn(return_raw)]
pub fn find(text: &str, pattern: &str) -> Result<String, Box<EvalAltResult>> {
let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?;
match re.find(text).map(|m| m.as_str().to_string()) {
Some(s) => Ok(s),
None => Ok(String::new()),
}
}
/// Returns all matches of a regex pattern in the text.
///
/// # Arguments
///
/// * `text` - A string to be matched with the pattern.
/// * `pattern` - The pattern to match on the string.
///
/// # Returns
///
/// Returns aan array of strings containing the all the things that match the regex pattern in the provided text.
///
/// # Example
///
/// ```javascript
/// import "std::regex" as regex;
///
/// let result = regex::find("This is an example!", `\be\w*\b`);
/// print(result); // output: ["example"]
/// ```
#[rhai_fn(return_raw)]
pub fn find_all(text: &str, pattern: &str) -> Result<Array, Box<EvalAltResult>> {
let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?;
let results: Array =
re.find_iter(text).map(|m| Dynamic::from(m.as_str().to_string())).collect();
Ok(results)
}
/// Replaces all matches of a regex pattern with a replacement string.
///
/// # Arguments
///
/// * `text` - A string to be matched with the pattern.
/// * `pattern` - The pattern to match on the string.
/// * `replacement` - A string that the matched pattern will get replaced with.
///
/// # Returns
///
/// Returns the provided text with the matches of the regex pattern replaced with the provided replacement argument.
///
/// # Example
///
/// ```javascript
/// import "std::regex" as regex;
///
/// let result = regex::replace("This is an example!", "an example", "a test");
/// print(result); // output: "This is a test!"
/// ```
#[rhai_fn(return_raw)]
pub fn replace(
text: &str,
pattern: &str,
replacement: &str,
) -> Result<String, Box<EvalAltResult>> {
let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?;
Ok(re.replace_all(text, replacement).to_string())
}
}

View File

@@ -16,7 +16,7 @@ pub mod text {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::text" as text;
///
/// let result = text::to_slug("Hello World!");
@@ -48,7 +48,7 @@ pub mod text {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::text" as text;
///
/// let result = text::to_camel_case("hello world example");
@@ -93,7 +93,7 @@ pub mod text {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::text" as text;
///
/// let result = text::truncate_chars("Hello World!", 5);
@@ -118,7 +118,7 @@ pub mod text {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::text" as text;
///
/// let result = text::to_upper("hello");
@@ -140,7 +140,7 @@ pub mod text {
///
/// # Example
///
/// ```js
/// ```javascript
/// import "std::text" as text;
///
/// let result = text::to_lower("HELLO");

View File

@@ -30,6 +30,7 @@ use tokio::sync::watch;
pub fn handle_listen(
var_name: String,
props: &Map,
shell: String,
store: ReactiveVarStore,
tx: tokio::sync::mpsc::UnboundedSender<String>,
) {
@@ -76,7 +77,7 @@ pub fn handle_listen(
tokio::spawn(async move {
let mut child = unsafe {
Command::new("/bin/sh")
Command::new(&shell)
.arg("-c")
.arg(&cmd)
// .kill_on_drop(true)
@@ -86,9 +87,41 @@ pub fn handle_listen(
.pre_exec(|| {
let _ = setpgid(Pid::from_raw(0), Pid::from_raw(0));
// chatgpt gave me this thing saying that it will kill all
// children when my program dies. I dont think this will work...
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM);
#[cfg(target_os = "linux")]
{
if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) != 0 {
log::error!(
"prctl PR_SET_PDEATHSIG failed: {}",
std::io::Error::last_os_error()
);
}
}
#[cfg(target_os = "freebsd")]
{
use libc::{c_int, c_void};
const PROC_PDEATHSIG_CTL: c_int = 11;
let sig: c_int = libc::SIGTERM;
if libc::procctl(
libc::P_PID,
0,
PROC_PDEATHSIG_CTL,
&sig as *const _ as *mut c_void,
) != 0
{
log::error!(
"procctl PROC_PDEATHSIG_CTL failed: {}",
std::io::Error::last_os_error()
);
}
}
#[cfg(target_os = "macos")]
{
// Perhaps make it a TODO?
log::warn!("Parent-death signal is not supported on macOS system");
}
Ok(())
})

View File

@@ -0,0 +1,249 @@
use super::{get_prefered_shell, handle_listen, handle_poll};
use crate::parser::ParseConfig;
use gtk4::glib;
use gtk4::prelude::*;
use gtk4::subclass::prelude::*;
use once_cell::sync::Lazy;
use rhai::Map;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::{Arc, RwLock};
mod imp {
use super::*;
#[derive(Default)]
pub struct LocalDataBinder {
pub value: RefCell<String>,
}
#[glib::object_subclass]
impl ObjectSubclass for LocalDataBinder {
const NAME: &'static str = "LocalDataBinder";
type Type = super::LocalDataBinder;
type ParentType = glib::Object;
}
impl ObjectImpl for LocalDataBinder {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: once_cell::sync::Lazy<Vec<glib::ParamSpec>> =
once_cell::sync::Lazy::new(|| {
vec![glib::ParamSpecString::builder("value")
.nick("Value")
.blurb("The bound value")
.default_value(None)
.build()]
});
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"value" => self.value.borrow().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"value" => {
let val: Option<String> = value.get().unwrap();
self.set_value(&self.obj(), val.unwrap_or_default());
}
_ => unimplemented!(),
}
}
}
impl LocalDataBinder {
pub fn set_value(&self, obj: &super::LocalDataBinder, val: String) {
*self.value.borrow_mut() = val;
obj.notify("value");
}
}
}
glib::wrapper! {
pub struct LocalDataBinder(ObjectSubclass<imp::LocalDataBinder>);
}
impl LocalDataBinder {
pub fn new() -> Self {
glib::Object::new::<Self>()
}
pub fn value(&self) -> String {
self.imp().value.borrow().clone()
}
pub fn set_value(&self, val: &str) {
self.set_property("value", val);
}
}
#[derive(Debug, Clone)]
pub struct LocalSignal {
pub id: u64,
pub props: Map,
pub data: Arc<LocalDataBinder>,
}
thread_local! {
pub static LOCAL_SIGNALS: Lazy<RefCell<HashMap<u64, Rc<LocalSignal>>>> =
Lazy::new(|| RefCell::new(HashMap::new()));
}
pub fn register_signal(id: u64, signal: Rc<LocalSignal>) -> Rc<LocalSignal> {
LOCAL_SIGNALS.with(|registry| {
let mut map = registry.borrow_mut();
map.entry(id).or_insert_with(|| signal.clone()).clone()
})
}
pub fn retain_signals(ids: &Vec<u64>) {
LOCAL_SIGNALS.with(|registry| {
let mut map = registry.borrow_mut();
map.retain(|id, _| ids.contains(id));
});
}
pub fn notify_all_localsignals() {
LOCAL_SIGNALS.with(|registry| {
let registry_ref = registry.borrow();
for (_, signal) in registry_ref.iter() {
signal.data.notify("value");
}
});
}
pub fn handle_localsignal_changes(
parser: Rc<RefCell<ParseConfig>>,
ast: Option<Rc<RefCell<rhai::AST>>>,
) {
let shell = get_prefered_shell();
let get_string_fn = shared_utils::extract_props::get_string_prop;
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<String>();
let store = Arc::new(RwLock::new(HashMap::new()));
LOCAL_SIGNALS.with(|registry| {
let registry_ref = registry.borrow();
for (id, signal) in registry_ref.iter() {
let props = &signal.props;
if let Ok(initial_str) = get_string_fn(&props, "initial", None) {
signal.data.set_value(&initial_str);
}
match get_string_fn(&props, "type", None) {
Ok(signal_type) => match signal_type.to_ascii_lowercase().as_str() {
"poll" => handle_poll(
id.to_string(),
&props,
shell.clone(),
store.clone(),
tx.clone(),
),
"listen" => handle_listen(
id.to_string(),
&props,
shell.clone(),
store.clone(),
tx.clone(),
),
o => log::error!("Invalid type: '{}'", o),
},
Err(_) => {
log::error!(
"Unable to handle localsignal {}: 'type' property missing or invalid.",
id
);
}
}
}
});
glib::MainContext::default().spawn_local(async move {
while let Some(id_str) = rx.recv().await {
let value_opt = {
let guard = store.read().unwrap();
guard.get(&id_str).cloned()
};
if let Some(value) = value_opt {
if let Ok(id) = id_str.parse::<u64>() {
LOCAL_SIGNALS.with(|registry| {
let mut registry_ref = registry.borrow_mut();
if let Some(signal) = registry_ref.get_mut(&id) {
let original = value.to_string();
let mut current = original.clone();
let mutations: Vec<rhai::FnPtr> = match signal.props.get("mutations") {
Some(v) => {
if let Ok(arr) = v.as_array_ref() {
arr.iter()
.filter_map(|item| {
item.clone().try_cast::<rhai::FnPtr>().or_else(|| {
log::warn!("Non-function found in signal.props.mutations");
None
})
})
.collect()
} else {
log::warn!("Localsignal mutations property is not an array");
Vec::new()
}
}
None => Vec::new(),
};
if mutations.is_empty() {
signal.data.set_value(&current);
return;
}
let parser_rc = parser.borrow_mut();
let compiled_ast = match ast.as_ref() {
Some(rc) => rc.borrow(),
None => {
log::warn!("No compiled AST available");
signal.data.set_value(&current);
return;
}
};
for mutation in mutations {
match mutation.call::<String>(&parser_rc.engine, &compiled_ast, (current.clone(),)) {
Ok(v) => {
current = v;
}
Err(e) => {
log::warn!(
"Signal {} mutation failed ({}), reverting to original value",
id,
e
);
current = original.clone();
break;
}
}
}
signal.data.set_value(&current);
} else {
log::warn!("No LocalSignal found for id {}", id);
}
});
} else {
log::error!("Invalid id_str '{}': cannot parse to u64", id_str);
}
} else {
log::warn!("No value found in store for id '{}'", id_str);
}
}
});
}

View File

@@ -15,12 +15,16 @@
*/
mod listen;
mod localsignal;
mod poll;
pub use localsignal::*;
use crate::ast::WidgetNode;
use listen::handle_listen;
use once_cell::sync::Lazy;
use poll::handle_poll;
use std::process::Command;
use std::sync::Mutex;
use std::{collections::HashMap, sync::Arc, sync::RwLock};
use tokio::sync::mpsc::UnboundedSender;
@@ -30,22 +34,30 @@ pub type ReactiveVarStore = Arc<RwLock<HashMap<String, String>>>;
pub static SHUTDOWN_REGISTRY: Lazy<Mutex<Vec<watch::Sender<bool>>>> =
Lazy::new(|| Mutex::new(Vec::new()));
pub fn get_prefered_shell() -> String {
// Check Dash and prefer if dash is installed.
let dash_installed: bool =
Command::new("which").arg("dash").output().map(|o| o.status.success()).unwrap_or(false);
let shell = if dash_installed { String::from("/bin/dash") } else { String::from("/bin/sh") };
shell
}
pub fn handle_state_changes(
root_node: &WidgetNode,
tx: UnboundedSender<String>,
) -> ReactiveVarStore {
// Enter node is the WidgetNode of Enter()
// it is the very root of every config.
let store: ReactiveVarStore = Arc::new(RwLock::new(HashMap::new()));
store: ReactiveVarStore,
) {
let shell = get_prefered_shell();
if let WidgetNode::Enter(children) = root_node {
for child in children {
match child {
WidgetNode::Poll { var, props } => {
handle_poll(var.to_string(), props, store.clone(), tx.clone());
handle_poll(var.to_string(), props, shell.clone(), store.clone(), tx.clone());
}
WidgetNode::Listen { var, props } => {
handle_listen(var.to_string(), props, store.clone(), tx.clone());
handle_listen(var.to_string(), props, shell.clone(), store.clone(), tx.clone());
}
_ => {}
}
@@ -53,8 +65,6 @@ pub fn handle_state_changes(
} else {
log::warn!("Expected Enter() as root node for config");
}
store
}
pub fn kill_state_change_handler() {

View File

@@ -26,6 +26,7 @@ use tokio::time::sleep;
pub fn handle_poll(
var_name: String,
props: &Map,
shell: String,
store: ReactiveVarStore,
tx: tokio::sync::mpsc::UnboundedSender<String>,
) {
@@ -57,7 +58,7 @@ pub fn handle_poll(
tokio::spawn(async move {
// Spawn a persistent shell
let mut child = match Command::new("/bin/sh")
let mut child = match Command::new(&shell)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()

View File

@@ -4,14 +4,12 @@ version = "0.1.0"
authors = ["byson94 <byson94wastaken@gmail.com>"]
edition = "2021"
license = "GPL-3.0-or-later"
description = "Utility crate used in eww"
repository = "https://github.com/elkowar/eww"
homepage = "https://github.com/elkowar/eww"
description = "Utility crate used in ewwii"
repository = "https://github.com/ewwii-sh/ewwii"
homepage = "https://github.com/ewwii-sh/ewwii"
[dependencies]
serde.workspace = true
derive_more.workspace = true
ref-cast.workspace = true
chrono = { workspace = true, features = ["unstable-locales"] }
rhai = { version = "1.22.2" }
anyhow.workspace = true
rhai = { workspace = true, features = ["internals"] }
anyhow.workspace = true
once_cell.workspace = true

View File

@@ -63,6 +63,12 @@ pub fn get_i32_prop(props: &Map, key: &str, default: Option<i32>) -> Result<i32>
if let Some(value) = props.get(key) {
if let Some(v) = value.clone().try_cast::<i32>() {
Ok(v)
} else if let Some(v) = value.clone().try_cast::<i64>() {
if v >= i32::MIN as i64 && v <= i32::MAX as i64 {
Ok(v as i32)
} else {
Err(anyhow!("Value for `{}` is out of range for i32", key))
}
} else if let Some(s) = value.clone().try_cast::<String>() {
s.parse::<i32>()
.map_err(|_| anyhow!("Expected property `{}` to be an i32 or numeric string", key))
@@ -113,12 +119,18 @@ pub fn get_duration_prop(props: &Map, key: &str, default: Option<Duration>) -> R
let mins =
num.parse::<u64>().map_err(|_| anyhow!("Invalid min value: '{}'", key_str))?;
Ok(Duration::from_secs(mins * 60))
} else if key_str.ends_with("m") {
let num = &key_str[..key_str.len() - 1];
let mins = num.parse::<u64>().map_err(|_| anyhow!("Invalid m value: '{}'", key_str))?;
Ok(Duration::from_secs(mins * 60))
} else if key_str.ends_with("h") {
let num = &key_str[..key_str.len() - 1];
let hrs = num.parse::<u64>().map_err(|_| anyhow!("Invalid h value: '{}'", key_str))?;
Ok(Duration::from_secs(hrs * 3600))
} else {
Err(anyhow!("Unsupported duration format: '{}'", key_str))
default.ok_or_else(|| {
anyhow!("Unsupported duration format: '{}', and no default provided", key_str)
})
}
} else {
default.ok_or_else(|| anyhow!("No value for duration and no default provided"))

View File

@@ -1,8 +1,4 @@
pub mod extract_props;
pub mod locale;
pub mod span;
pub mod wrappers;
pub use locale::*;
pub use span::*;
pub use wrappers::*;

View File

@@ -1,11 +0,0 @@
use chrono::Locale;
use std::env::var;
/// Returns the `Locale` enum based on the `LC_ALL`, `LC_TIME`, and `LANG` environment variables in
/// that order, which is the precedence order prescribed by Section 8.2 of POSIX.1-2017.
/// If the environment variable is not defined or is malformed use the POSIX locale.
pub fn get_locale() -> Locale {
var("LC_ALL").or_else(|_| var("LC_TIME")).or_else(|_| var("LANG")).map_or(Locale::POSIX, |v| {
v.split('.').next().and_then(|x| x.try_into().ok()).unwrap_or_default()
})
}

View File

@@ -1,89 +0,0 @@
use derive_more::{Debug, *};
use ref_cast::RefCast;
use serde::{Deserialize, Serialize};
/// The name of a variable
#[repr(transparent)]
#[derive(
Clone,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
AsRef,
From,
FromStr,
Display,
Debug,
RefCast,
)]
#[debug("VarName({})", _0)]
pub struct VarName(pub String);
impl std::borrow::Borrow<str> for VarName {
fn borrow(&self) -> &str {
&self.0
}
}
impl AttrName {
pub fn to_attr_name_ref(&self) -> &AttrName {
AttrName::ref_cast(&self.0)
}
}
impl From<&str> for VarName {
fn from(s: &str) -> Self {
VarName(s.to_owned())
}
}
impl From<AttrName> for VarName {
fn from(x: AttrName) -> Self {
VarName(x.0)
}
}
/// The name of an attribute
#[repr(transparent)]
#[derive(
Clone,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
AsRef,
From,
FromStr,
Display,
Debug,
RefCast,
)]
#[debug("AttrName({})", _0)]
pub struct AttrName(pub String);
impl AttrName {
pub fn to_var_name_ref(&self) -> &VarName {
VarName::ref_cast(&self.0)
}
}
impl std::borrow::Borrow<str> for AttrName {
fn borrow(&self) -> &str {
&self.0
}
}
impl From<&str> for AttrName {
fn from(s: &str) -> Self {
AttrName(s.to_owned())
}
}
impl From<VarName> for AttrName {
fn from(x: VarName) -> Self {
AttrName(x.0)
}
}

1
docs/.gitignore vendored
View File

@@ -1 +0,0 @@
book/

View File

@@ -1,3 +0,0 @@
# Ewwii docs
Ewwii documentation page.

View File

@@ -1,14 +0,0 @@
[book]
authors = ["byson94"]
language = "en"
multilingual = false
src = "src"
title = "Ewwii documentation"
[output.html]
default-theme = "latte"
preferred-dark-theme = "ewwii"
site-url = "/ewwii/"
git-repository-url = "https://github.com/Ewwii-sh/ewwii"
additional-js = ["./js/home_button.js", "./theme/rhai-autodocs/tabs.js"]
additional-css = ["./theme/catppuccin.css", "./theme/ewwii.css", "./theme/rhai-autodocs/default.css"]

View File

@@ -1,31 +0,0 @@
document.addEventListener("DOMContentLoaded", function () {
const homeBtn = document.createElement("a");
homeBtn.href = "https://ewwii-sh.github.io/";
homeBtn.innerText = "🏠︎";
homeBtn.className = "home-button";
homeBtn.style.padding = "6px";
homeBtn.style.backgroundColor = "transparent";
homeBtn.style.color = "grey";
homeBtn.style.borderRadius = "4px";
homeBtn.style.textDecoration = "none";
homeBtn.style.display = "inline-flex";
homeBtn.style.alignItems = "center";
homeBtn.style.justifyContent = "center";
homeBtn.style.transition = "all 0.2s ease";
// Hover effect
homeBtn.addEventListener("mouseover", () => {
homeBtn.style.color = "#bbbbbb";
homeBtn.style.transform = "scale(1.01)";
});
homeBtn.addEventListener("mouseout", () => {
homeBtn.style.backgroundColor = "transparent";
homeBtn.style.color = "grey";
homeBtn.style.transform = "scale(1)";
});
const header = document.querySelector(".right-buttons");
if (header) header.appendChild(homeBtn);
});

View File

@@ -1,44 +0,0 @@
# Summary
[Introduction](introduction.md)
- [Getting Started](getting_started.md)
- [Installation](installation.md)
- [Configuration & Syntax](config/config_and_syntax.md)
- [Configuration](config/configuration.md)
- [Fundamentals](config/config_fundamentals.md)
- [Variables](config/variables.md)
- [Expression Language](config/expression_language.md)
- [Rendering and Best Practices](config/rendering_and_best_practices.md)
- [Widgets](widgets/widgets.md)
- [Widgets & Parameters](widgets/widgets_and_params.md)
- [Widget Properties](widgets/props.md)
- [Theming & UI](theming/theming_and_ui.md)
- [Working With GTK](theming/working_with_gtk.md)
- [Styling Widgets](theming/styling_widgets.md)
- [Modules](modules/modules.md)
- [User Defined Modules](modules/user_defined.md)
- [Global Builtin Rhai Functions](modules/global.md)
- [Std Library](modules/stdlib.md)
- [API Library](modules/apilib.md)
- [Examples](examples/examples.md)
- [Starter Bar](examples/starter_bar.md)
- [Wifi Manager Template](examples/wifi_manager.md)
- [Advanced Commands](commands/advanced_commands_intro.md)
- [Update](commands/update.md)
- [Call Functions](commands/call_fns.md)
- [Troubleshooting](troubleshooting.md)

View File

@@ -1,9 +0,0 @@
# Advanced Commands
Now that you all are familiar with ewwii and may be making awesome widgets, lets get to know about some advanced commands in ewwii, that makes it even more flexible and powerful.
In this section, we will cover:
- How to use advanced commands
- Limitations of these advanced commands
- And finally, solutions to these limitations

View File

@@ -1,44 +0,0 @@
# `ewwii call-fns`
The `call-fns` command allows you to call **Rhai functions** directly from the command line. This can be useful for triggering specific functionality in your widget configuration without updating or interacting with the GUI.
> **Note:** All variables created by `poll` or `listen` handlers will default to their initial values when calling functions. They **cannot be preserved**.
**Example:**
```bash
# Call a single Rhai function
ewwii call-fns "my_function(32, 21)"
# Call multiple functions
ewwii call-fns "first_function('string')" "second_function()"
```
This will execute the specified functions in the context of your current configuration.
## Limitation: Main configuration
However, there is one limitation in `call-fns`. It can only see and call functions defined within your main configuration (i.e `ewwii.rhai` file).
## Solution: Creating wrappers
But, there is one solution though! You can call external functions from functions defined in main.
**Example:**
```js
// in external.rhai
fn awesome_fn() {
print("Awesome fn triggered!");
}
```
```js
// in ewwii.rhai
fn call_awesome_fn() {
import "external" as external;
external::awesome_fn();
}
```
Now you can run `ewwii call-fns "call-awesome_fn()"` and get "Awesome fn triggered!".

View File

@@ -1,37 +0,0 @@
# `ewwii update`
The `update` command refreshes the widgets rendered in a specified window. It allows you to immediately reflect changes in your widget configuration.
**Example:**
```bash
# Update a single window
ewwii update --window "my_awesome_widget"
# Short form
ewwii update -w "my_awesome_widget"
```
## Limitation: `poll` and `listen` handlers
A known limitation of `ewwii update` is that it does **not** preserve variables created by `poll` or `listen` handlers. When you run `ewwii update`, these variables are reset to their initial values.
## Solution: Injecting variables with `--inject-vars`
To prevent certain variables from being reset, you can manually inject values into the configuration using the `--inject-vars` argument. This allows you to explicitly set variable values during an update.
```bash
# Long form
ewwii update --window "my_awesome_widget" --inject-vars "VAR1=bar,VAR2=foo2"
# Short form
ewwii update -w "my_awesome_widget" --inject-vars "VAR1=ewwii,VAR2=baz1"
```
> Note: `--inject-vars` does **not** automatically capture the current state of `poll` or `listen` variables. You must explicitly provide the values you want.
## Best Practices
- Consider using `--inject-vars` when your widgets rely on `poll` or `listen` to avoid default resets.
- Update multiple widgets by running `ewwii update` for each window separately.
- Be explicit and consistent with variable values to avoid unexpected behavior.

View File

@@ -1,14 +0,0 @@
# Configuration & Syntax
This section introduces the foundational systems that define how you configure your UI, express logic, and work with dynamic data in Rahi (Ewwii's configuration language).
The configuration model is imparative by nature but will be used in a declarative format that is made to make configuring ewwii easy as well as to provide a logical way of configuring. You'll also work with special built in functions and modules that expose live system state and other information.
We'll cover:
- The structure of configuration files
- Embedding expressions within properties and attributes
- Accessing and using built-in variables
- Interpolation, scoping, and reactivity rules
> If you're coming from EWW's Yuck, expect similarities in structure but with much more flexibility and logical programming.

View File

@@ -1,119 +0,0 @@
# Fundamentals
Ewwii uses Rhai as its configuration language. But instead of just pure Rhai, ewwii has its own layout that users should follow to create widgets using Rhai. And you may be wondering why ewwii has a "custom" layout instead of allowing users to just use pure Rhai. It's good question and the reason why ewwii has a custom layout is because it tries to remove unnecessary complexity.
The full reasons for this layout wont be explained much more because it goes way deeper than just "decreasing complexity".
For more information about Rhai, you can read [their documentation](https://rhai.rs/book/).
## Widgets and their parameters
Each widget in ewwii is a function (e.g: `button(#{...})` is a function call to create a button). So each one requires its own parameters.
For example, `defwindow` expects a **String**, **Properties**, and a function call that **returns a widget.**
**Example:**
```js
enter([
// the string here (the text in "") is the name of the window
// the content in #{} is the properties
// and the 3rd `root_widget()` call is the function that returns a child.
// defwindow cant have children in [] directly, but it expects a function returning it for it.
defwindow("example", #{
monitor: 0,
windowtype: "dock",
stacking: "fg",
wm_ignore: false,
geometry: #{
x: "0%",
y: "2px",
width: "90%",
height: "30px",
anchor: "top center"
},
reserve: #{ distance: "40px" side: "top" }
}, root_widget())
])
```
This is not that complex once you know the parameters of defwindow as most of the other widgets only take in properties or optinally children. **Poll/Listen** are the only things that is similar to `defwindow` and you will learn about it later in the [variables chapter](./variables.md).
## The root
It is an important concept that users must know to go forward with this documentaiton. Ewwii's Rhai layout is made to be **logical and powerful**, so the user is given access the root of the entire widget system.
The root is defined as `enter()` and it is where you should write `defwindow`.
Here is an example:
```js
enter([
defwindow("example", #{
monitor: 0,
windowtype: "dock",
stacking: "fg",
wm_ignore: false,
geometry: #{
x: "0%",
y: "2px",
width: "90%",
height: "30px",
anchor: "top center"
},
reserve: #{ distance: "40px" side: "top" }
}, root_widget())
])
```
Now that you saw this example, you may be wondering why we are doing `enter([])` instead of `enter()`. That is due to another fundamental concept in ewwii which is very important. You will learn about it in the [properties and child definition section](#properties-and-child-definition).
## Semi-colons
Semi-colon is an important character in Rhai. Just like programming languages like JavaScript, Java, Rust etc.
You can use the following link to read about semi-colons in the Rhai book as they have an awesome documentation.
[https://rhai.rs/book/ref/statements.html#statements](https://rhai.rs/book/ref/statements.html#statements)
## Properties and child definition
The part where most people get confused is the use of `[]` and `#{}`. Let's get into what those are and how you can use them.
The `[]` is used for adding **children** to a widget.
**Example:**
```js
fn greeter(foo) {
return box(#{
orientation: "horizontal",
halign: "center"
}, [
// the `[]` holds a button which is the child widget of the box widget
// each element in a `[]` should end in a comma (,) instead of a semi-colon (;).
button(#{ onclick: `notify-send '${foo}'`, label: "baz" }),
label(#{ text: "example" }),
]);
};
```
The `#{}` works similar to the `[]` but, it is used to add properties into the widget.
**Example:**
```js
fn greeter(foo) {
// the `#{}` holds the properties of the box widget
// each element in a `#{}` should end in a comma (,) instead of a semi-colon (;).
return box(#{
orientation: "horizontal",
halign: "center"
}, [
// properties are assigned to both button and label using the #{}.
button(#{ onclick: `notify-send '${foo}'`, label: "baz" }),
label(#{ text: "example" }),
]);
};
```

View File

@@ -1,145 +0,0 @@
# Writing your ewwii configuration
(For a list of all built-in widgets (i.e. `box`, `label`, `button`), see [Widget Documentation](../widgets/widgets.md).)
Ewwii is configured using a language called `Rhai`.
Using rhai, you declare the structure and content of your widgets, the geometry, position, and behavior of any windows,
as well as any state and data that will be used in your widgets.
Rhai is based around imparative syntax, which you may know from programming languages like C, Rust etc.
If you're using vim, you can make use of [vim-rhai](https://github.com/rhaiscript/vim-rhai) for editor support.
If you're using VSCode, you can get syntax highlighting and formatting from [vscode-rhai](https://marketplace.visualstudio.com/items?itemName=rhaiscript.vscode-rhai).
Additionally, any styles are defined in CSS or SCSS (which is mostly just slightly improved CSS syntax).
While ewwii supports a significant portion of the CSS you know from the web,
not everything is supported, as ewwii relies on GTK's own CSS engine.
Notably, some animation features are unsupported,
as well as most layout-related CSS properties such as flexbox, `float`, absolute position or `width`/`height`.
To get started, you'll need to create two files: `ewwii.rhai` and `ewwii.scss` (or `ewwii.css`, if you prefer).
These files must be placed under `$XDG_CONFIG_HOME/ewwii` (this is most likely `~/.config/ewwii`).
Now that those files are created, you can start writing your first widget!
## Creating your first window
Firstly, you will need to create a top-level window. Here, you configure things such as the name, position, geometry, and content of your window.
Let's look at an example window definition:
```js
enter([ // Add all defwindow inside enter. Enter is the root of the config.
defwindow("example", #{
monitor: 0,
windowtype: "dock",
stacking: "fg",
wm_ignore: false,
geometry: #{
x: "0%",
y: "2px",
width: "90%",
height: "30px",
anchor: "top center"
},
reserve: #{ distance: "40px" side: "top" }
}, label(#{ text: "example content" }))
])
```
Here, we are defining a window named `example`, which we then define a set of properties for. Additionally, we set the content of the window to be the text `"example content"`.
You can now open your first window by running `ewwii open example`! Glorious!
### `defwindow`-properties
| Property | Description |
| ---------: | ------------------------------------------------------------------------ |
| `monitor` | Which monitor this window should be displayed on. See below for details. |
| `geometry` | Geometry of the window. |
**`monitor`-property**
This field can be:
- the string `<primary>`, in which case ewwii tries to identify the primary display (which may fail, especially on wayland)
- an integer, declaring the monitor index
- the name of the monitor
- a string containing a JSON-array of monitor matchers, such as: `'["<primary>", "HDMI-A-1", "PHL 345B1C", 0]'`. Ewwii will try to find a match in order, allowing you to specify fallbacks.
**`geometry`-properties**
| Property | Description |
| ----------------: | ----------------------------------------------------------------------------------------------------------------------- |
| `x`, `y` | Position of the window. Values may be provided in `px` or `%`. Will be relative to `anchor`. |
| `width`, `height` | Width and height of the window. Values may be provided in `px` or `%`. |
| `anchor` | Anchor-point of the window. Either `center` or combinations of `top`, `center`, `bottom` and `left`, `center`, `right`. |
| `resizable` | Whether to allow resizing the window or not. Eiither `true` or `false`. |
<br/>
Depending on if you are using X11 or Wayland, some additional properties exist:
#### X11
| Property | Description |
| -----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `stacking` | Where the window should appear in the stack. Possible values: `fg`, `bg`. |
| `wm_ignore` | Whether the window manager should ignore this window. This is useful for dashboard-style widgets that don't need to interact with other windows at all. Note that this makes some of the other properties not have any effect. Either `true` or `false`. |
| `reserve` | Specify how the window manager should make space for your window. This is useful for bars, which should not overlap any other windows. |
| `windowtype` | Specify what type of window this is. This will be used by your window manager to determine how it should handle your window. Possible values: `normal`, `dock`, `toolbar`, `dialog`, `desktop`. Default: `dock` if `reserve` is specified, `normal` otherwise. |
#### Wayland
| Property | Description |
| ----------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `stacking` | Where the window should appear in the stack. Possible values: `fg`, `bg`, `overlay`, `bottom`. |
| `exclusive` | Whether the compositor should reserve space for the window automatically. Either `true` or `false`. If `true` `:anchor` has to include `center`. |
| `focusable` | Whether the window should be able to be focused. This is necessary for any widgets that use the keyboard to work. Possible values: `none`, `exclusive` and `ondemand`. |
| `namespace` | Set the wayland layersurface namespace ewwii uses. Accepts a `string` value. |
## Your first widget
While our bar is already looking great, it's a bit boring. Thus, let's add some actual content!
```js
fn greeter(name) {
return box(#{
orientation: "horizontal",
halign: "center"
}, [
button(#{ onclick: `notify-send 'Hello' 'Hello, ${name}'`, label: "Greet" })
]);
};
```
To show this, let's replace the text in our window definition with a call to this new widget:
```js
enter([
defwindow(
"example",
#{
// ... properties omitted
},
greeter("Bob")
),
]);
```
There is a lot going on here, so let's step through this.
We are creating a function named `greeter` and a function is equal to a component that returns a child (widget). So function has two uses: one to return a component, and the other to do a set of functions.
And this function takes one parameters, called `name`. The `name` parameter _must_ be provided or else, you should emit it. Rhai does allow adding optional parameters, but we will talk about it later for the sake of beginners who are in-experienced with imprative programming languages.
Now inside the function, we declare the body of our widget that we are returning. We make use of a `box`, which we set a couple properties of.
We need this `box`, as a function can only ever contain a single widget - otherwise,
ewwii would not know if it should align them vertically or horizontally, how it should space them, and so on.
Thus, we wrap multiple children in a `box`.
This box then contains a button.
In that button's `onclick` property, we refer to the provided `name` using string-interpolation syntax: `` `${name}` ``. It is not possible to use a variable within a `""` or `''` just like javascript. You can learn more about it [here](https://rhai.rs/book/ref/strings-chars.html?interpolation#string-interpolation).
<!-- TODO -->
<!-- In fact, there is a lot more you can do within `${...}` - more on that in the chapter about the [expression language](expression_language.md). -->
To then use our widget, we call the function that provides the widget with the necessary parameters passed.
As you may have noticed, we are using a couple predefined widgets here. These are all listed and explained in the [widgets chapter](widgets.md).

View File

@@ -1,87 +0,0 @@
# The Rhai Expression Engine
Ewwii uses [Rhai](https://rhai.rs/) as its core expression and scripting engine. This means your configuration is more than just static values or simple substitutions—its **real code**, and you can use it anywhere dynamic behavior is needed.
Rhai expressions can:
- Perform logic and branching (`if`, `match`, `? :`)
- Handle mathematical calculations and string operations
- Access nested data from arrays, maps, or JSON
- Run custom functions
- Be used directly in layout definitions, widget attributes, and style rules
Unlike Yuck, where expressions were embedded in `{ ... }` or `${ ... }`, Rhai treats expressions as **native**. They dont need to be wrapped in special delimiters. If you can write it in Rhai, it just works.
## Example
```js
let value = 12 + foo * 10;
box(#{
class: "baz"
orientation: "h",
}, [
button(#{
class: button_active ? "active" : "inactive",
on_click: "toggle_thing",
label: `Some math: ${value}`,
}),
]);
```
## Core Features
Rhai supports a wide range of expression capabilities:
- **Mathematics**: `+`, `-`, `*`, `/`, `%`
- **Comparisons**: `==`, `!=`, `<`, `>`, `<=`, `>=`
- **Boolean logic**: `&&`, `||`, `!`
- **Conditionals**: `if/else`, ternary (`cond ? a : b`)
- **Regex matching**: `=~` operator (Rust-style regex)
- **Optional access**: `?.` and `?.[index]`
- **Data structures**: maps/arrays (`obj.field`, `arr[3]`, `map["key"]`)
- **Function calls**: standard and Ewwii-specific built-ins (see below)
- **String interpolation**: `` `Value is ${value}` `` (standard Rhai feature)
> Note
>
> ---
>
> Rhai only allows string interpolation only for the strings that are quoted in `` similar to JavaScript.
>
> > Learn more about it [here](https://rhai.rs/book/ref/strings-chars.html?interpolation#string-interpolation).
## Common Built-in Functions
💡 _You may recognize some of these from the old expression system—but now they're just Rhai functions._
Examples include:
- `round()`, `floor()`, `ceil()`, `powi()`, `powf()`
- `min()`, `max()`, `sin()`, `cos()`, etc.
- `replace()`, `matches()`, `captures()`
- `strlength()`, `arraylength()`, `objectlength()`
- `jq()` run jaq-compatible filters on JSON data
- `get_env()` read environment variables
- `formattime()` format UNIX timestamps
- `formatbytes()` format file sizes (IEC or SI)
## Dynamic Usage
Because expressions are just Rhai, you can now write real logic inline or break it into reusable functions:
```js
fn status_text(active) {
return active ? "enabled" : "disabled";
}
label({
text: `Status: ${status_text(system_active)}`
});
```
---
### TL;DR
> If youve used scripting languages like JavaScript or Lua, youll feel at home. Rhai gives you real control—not just interpolation tricks.

View File

@@ -1,133 +0,0 @@
# Rendering and Best Practices
## Rendering children in your widgets
As your configuration grows, you might want to improve its structure by factoring out pieces into reusable functions.
In Ewwiis Rhai-based configuration system, you can define wrapper functions that return widgets and accept a `children` parameter (or any parameter that you prefer), just like built-in widgets such as `box()` or `button()`.
Here's an example of a custom container that adds a label before its children:
```js
fn labeled_container(name, children = []) {
return box(#{ class: "container" }, [label(#{text: name})] + children)
}
```
You can call it like this:
```js
labeled_container("foo", [
button(#{ onclick: "notify-send hey ho", label: "Click me" }),
]);
```
Because children are just a list of widgets, you can also write functions that structure them however you'd like. For example, here's a layout that places the first two children side by side:
```js
fn two_boxes(children = []) {
return box(#{}, [
box(#{ class: "first" }, [children[0]]),
box(#{ class: "second" }, [children[1]])
]);
}
```
And call it like this:
```js
two_boxes([label(#{ text: "First" }), label(#{ text: "Second" })]);
```
If a child is missing (e.g., children[1] doesn't exist), make sure to handle that gracefully or document the expected number of children.
<!-- TODO: add it once literal is implemented -->
<!-- ## Dynamically generated widgets with `literal`
In some cases, you want to not only change the text,
value, or color of a widget dynamically, but instead want to generate an entire widget structure dynamically.
This is necessary if you want to display lists of things (for example notifications)
where the amount is not necessarily known,
or if you want to change the widget structure in some other, more complex way.
For this, you can make use of one of ewwii's most powerful features: the `literal` widget.
```js
let variable_containing_rhai = "(box (button 'foo') (button 'bar'))";
// Then, inside your widget, use:
literal(#{ content: variable_containing_rhai })
```
Here, you specify the content of your literal by providing it a string (most likely stored in a variable) which contains a single yuck widget tree.
Ewwii then reads the provided value and renders the resulting widget. Whenever it changes, the widget will be rerendered.
Note that this is not all that efficient. Make sure to only use `literal` when necessary! -->
## Window ID
In some cases you may want to use the same window configuration for multiple widgets, e.g. for multiple windows. This is where arguments and ids come in.
Firstly let us start off with ids. An id can be specified in the `open` command
with `--id`, by default the id will be set to the name of the window
configuration. These ids allow you to spawn multiple of the same windows. So
for example you can do:
```bash
ewwii open my_bar --screen 0 --id primary
ewwii open my_bar --screen 1 --id secondary
```
## Generating a list of widgets from array using `for`
If you want to display a list of values, you can use the `for`-Element to fill a container with a list of elements generated from a JSON-array.
```js
let my_array = [1, 2, 3];
// Then, inside your widget, you can use
box(#{}, [
for entry in my_array {
button(#{ onclick: `notify-send 'click' 'button ${entry}'`, label: entry.to_string() })
}
])
```
This can be useful in many situations, for example when generating a workspace list from an array representation of your workspaces.
In many cases, this can be used instead of `literal`, and should most likely be preferred in those cases.
<!-- To see how to declare and use more advanced data structures, check out the [data structures example](/examples/data-structures/ewwii.rhai). -->
## Splitting up your configuration
As time passes, your configuration might grow larger and larger. Luckily, you can easily split up your configuration into multiple files!
There are two options to achieve this:
### Using `import/export`
```js
// in ./foo/baz.rhai
/// Note: all functions are automatically exported.
fn greet() { return "Greetings!" }
let PI = 3.14159
export PI; // we need to export variables manually
// in ./ewwii.rhai
import "foo/baz" as example;
print(example::greet()); // Greetings!
print(example::PI); // 3.14159
```
A rhai file may import the contents of any other rhai file that they export. For this, make use of the `import` directive. If you are exporting a variable/function, make use the `export` directive.
### Using a separate ewwii configuration directory
If you want to separate different widgets even further, you can create a new ewwii config folder anywhere else.
Then, you can tell ewwii to use that configuration directory by passing _every_ command the `--config /path/to/your/config/dir` flag.
Make sure to actually include this in all your `ewwii` calls, including `ewwii kill`, `eww logs`, etc.
This launches a separate instance of the ewwii daemon that has separate logs and state from your main ewwii configuration.
```bash
ewwii --config "/path/to/your/config/dir"
```

View File

@@ -1,122 +0,0 @@
# Variables
Now that you feel sufficiently greeted by your bar, you may realize that showing data like the time and date might be even more useful than having a button that greets you.
To implement dynamic content in your widgets, you make use of _variables_.
All variables are only locally available so you would need to pass it around using function parameters. And whenever the variable changes, the value in the widget will update!
## Static variables
In Rhai, all variables are dynamically typed bindings to values. You can define variables using let, pass them as function parameters.
**Basic variables (`let`)**
```js
let foo = "value";
```
This is the simplest type of variable.
Basic variables don't ever change automatically, if you need a dynamic variable, you can use built in functions like `poll()` and `listen()` to register dynamic values which we will talk about in the following section.
## Dynamic variables
Just having static variables that wont update is pretty limiting. So, ewwii has two built in functions to register dynamic variables that can change according to the command.
**Polling variables (`poll`)**
```js
enter([
poll("var_name", #{
// It is recommended to have initial property passed.
// If not provided, it will default to no value which may cause problems when used.
// You can pass something like "" if you want no initial value.
initial: "inital value",
interval: "2s",
cmd: "date +%H:%M:%S", // command to execute
});
])
```
A polling variable is a variable which runs a provided shell-script repeatedly, in a given interval.
This may be the most commonly used type of variable.
They are useful to access any quickly retrieved value repeatedly,
and thus are the perfect choice for showing your time, date, as well as other bits of information such as pending package updates, weather, and battery level.
But it is important to note that these variables are locally available only in enter (a.k.a the root) and you need to pass it to other functions with something like `some_fn(foo)` when you want to use a polled variable.
<!-- You can also specify an initial-value. This should prevent ewwii from waiting for the result of a given command during startup, thus
making the startup time faster. -->
To externally update a polling variable, `ewwii update` can be used like with basic variables to assign a value. [Learn more about ewwii update](../commands/update.md).
**Listening variables (`listen`)**
```js
enter([
listen("foo", #{
initial: "whatever",
cmd: "tail -F /tmp/some_file",
});
])
```
Listening variables might be the most confusing of the bunch.
A listening variable runs a script once, and reads its output continously.
Whenever the script outputs a new line, the value will be updated to that new line.
In the example given above, the value of `foo` will start out as `"whatever"`, and will change whenever a new line is appended to `/tmp/some_file`.
These are particularly useful when you want to apply changes instantaneously when an operation happens if you have a script
that can monitor some value on its own. Volume, brightness, workspaces that get added/removed at runtime,
monitoring currently focused desktop/tag, etc. are the most common usecases of this type of variable.
These are particularly efficient and should be preffered if possible.
For example, the command `xprop -spy -root _NET_CURRENT_DESKTOP` writes the currently focused desktop whenever it changes.
Another example usecase is monitoring the currently playing song with playerctl: `playerctl --follow metadata --format {{title}}`.
<!--
**Built-in "magic" variables**
In addition to defining your own variables, ewwii provides some values for you to use out of the box.
These include values such as your CPU and RAM usage.
These mostly contain their data as JSON, which you can then get using the [json access syntax](expression_language.md).
All available magic variables are listed [here](magic-vars.md). -->
<div class="warning">
<strong>Warning:</strong> Dynamic variables created by `poll` or `listen` handlers
should always be defined inside an <code>enter([])</code> block.
If `poll` or `listen` is defined outside the <code>enter([])</code> block, then they simply will be ignored.
</div>
## Passing variables
As we discussed earlier, all variables are only available locally. So, you would need to pass it around from the current scope.
Here is an example of how it is done:
```js
let foo = "example";
enter([
poll("time", #{
initial: "inital value",
interval: "2s",
cmd: "date +%H:%M:%S",
}),
defwindow("1", #{}, wont_work()), // wont work
defwindow("2", #{}, will_work(time, foo)) // will work
])
// Here we have 2 variables named "time" (registered dynamically by poll) and foo (a static variable)
// here is an example of something that wont
fn wont_work() {
return box(#{}, [ label(#{ text: time }), label(#{ text: foo }) ]);
}
// here is an example of something that will work
fn will_work(time, foo) { // time and foo is passed from `enter([])`
return box(#{}, [ label(#{ text: time }), label(#{ text: foo }) ]);
}
```

View File

@@ -1,13 +0,0 @@
# Examples
This section provides hands-on, practical demonstrations of everything covered so far. Real layouts, interactive components, and theming techniques will be used in these examples.
Each example is minimal but complete, focusing on a particular design pattern or technique. You can copy-paste them into your own config to experiment.
Included:
- Common layout patterns (e.g., bars, panels, dashboards)
- Interactive elements like buttons, sliders, toggles
- Theming techniques and tricks to override or enhance GTK styles
If you're stuck or just looking for inspiration, this is your go-to reference.

View File

@@ -1,15 +0,0 @@
# Starter Bar
![Example 1](https://raw.githubusercontent.com/ewwii-sh/ewwii/main/examples/ewwii-bar/ewwii-bar.png)
A basic starter bar which will be very helpful to beginners, see [examples/ewwii-bar](https://github.com/Ewwii-sh/ewwii/tree/main/examples/ewwii-bar).
## Installing
This template is regestered in the [eii-manifests](https://github.com/Ewwii-sh/eii-manifests), so you can install it via [eiipm](https://ewwii-sh.github.io/docs/package-manager/overview/): ewwii's package manager.
Just run the following command and have the template ready in the current working directory in an instant!
```bash
eiipm i starter_template
```

View File

@@ -1,15 +0,0 @@
# Wifi Manager Template
![Wifi Manager Template Screenshot](../images/wifi_manager_template.png)
An advanced ewwii template showcasing `api::wifi` module, manual `nm-cli` parsing, and advacned ewwii CLI commands. See the template at [Ewwii-sh/ewifi_gui_template](https://github.com/Ewwii-sh/ewifi_gui_template).
## Installing
This template is regestered in the [eii-manifests](https://github.com/Ewwii-sh/eii-manifests), so you can install it via [eiipm](https://ewwii-sh.github.io/docs/package-manager/overview/): ewwii's package manager.
Just run the following command and have the template ready in the current working directory!
```bash
eiipm i ewifi_gui_template
```

View File

@@ -1,3 +0,0 @@
# Getting Started
Getting starting with Ewwii. This section will cover how you can install and use ewwii.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -1,90 +0,0 @@
# Installation
The first step of using Ewwii is installing it. You would need to have the following prerequesties installed on your system to build/install ewwii.
**Prerequesties:**
- rustc
- cargo
Rather than with your system package manager,
I **strongly** recommend installing it using [rustup](https://rustup.rs/).
Additionally, eww requires some dynamic libraries to be available on your system.
The exact names of the packages that provide these may differ depending on your distribution.
The following list of package names should work for arch linux:
<details>
<summary><strong>Packages (click here)</strong></summary>
- gtk3 (libgdk-3, libgtk-3)
- gtk-layer-shell (only on Wayland)
- pango (libpango)
- gdk-pixbuf2 (libgdk_pixbuf-2)
- libdbusmenu-gtk3
- cairo (libcairo, libcairo-gobject)
- glib2 (libgio, libglib-2, libgobject-2)
- gcc-libs (libgcc)
- glibc
</details>
> **Note** that you will most likely need the -devel variants of your distro's packages to be able to compile ewwii.
## Building
Once you have the prerequisites ready, you're ready to install and build ewwii.
First clone the repo:
```bash
git clone https://github.com/Ewwii-sh/ewwii
```
```bash
cd ewwii
```
Then build:
```bash
cargo build --release --no-default-features --features x11
```
**NOTE:**
When you're on Wayland, build with:
```bash
cargo build --release --no-default-features --features=wayland
```
## Running ewwii
Once you've built it you can now run it by entering:
```bash
cd target/release
```
Then make the Eww binary executable:
```bash
chmod +x ./ewwii
```
Then to run it, enter:
```bash
./ewwii daemon
./ewwii open <window_name>
```
## Installing via package managers
If you don't want to go through the _very_ tedious task of cloning and building ewwii, you can install it using Cargo (Rust crate manager).
You can run the following command to install ewwii from cargo:
```bash
cargo install --git https://github.com/Ewwii-sh/ewwii
```

View File

@@ -1,26 +0,0 @@
# Ewwii - Widgets for everyone made better!
Ewwii (ElKowar's Wacky Widgets improved interface) is a foork of
Eww (ElKowar's Wacky Widgets) which is a
widget system made in [Rust](https://www.rust-lang.org/),
which lets you create your own widgets similarly to how you can in AwesomeWM.
**Strength of Ewwii over Eww:**
- Full-fledged scripting & expressions
- User-defined widget trees & composition
- Built-in configuration libraries
- Builtin tooling for better developer experience
- Full control over reactive updates (user defines if widget should update dynamically)
Ewwii is configured in [Rhai](https://rhai.rs/)
and themed using [CSS](https://en.wikipedia.org/wiki/CSS)
or [SCSS](<https://en.wikipedia.org/wiki/Sass_(style_sheet_language)>),
it is easy to customize and is powerful and dynamic.
The main goal of Ewwii is to make configuration easy
and to give the user all the power that they need.
Rhai is not just a basic markup language. It is a full embeddable scripting language!
This makes ewwii's configuration even more flexible and powerful.
Whether you're building a tiling-friendly status bar, a floating dashboard, or a themed control panel, Ewwii gives you the tools to design it, script it, and make it your own.

View File

@@ -1,6 +0,0 @@
# Magic variables
These are variables that are always there, without you having to import them.
The delay between all the updating variables except `EWW_TIME` is 2s, for `EWW_TIME` it is 1s.

View File

@@ -1 +0,0 @@
# API Library

View File

@@ -1 +0,0 @@
# Global Builtin Rhai Functions

View File

@@ -1,12 +0,0 @@
# Modules
Modules undoubtedly are one of the most powerful features in Rhai. They provide infinite extensibility to ewwii.
Every module follows the syntax:
```js
import "std::env" as env;
let home = env::get_home_dir(); // returns `$HOME` env var value
```
This allows you to write expressive, modular Rhai code with functions grouped logically under `std` or custom namespaces.

View File

@@ -1 +0,0 @@
# Std Library

View File

@@ -1,55 +0,0 @@
# User-Defined Modules
User-defined modules allow you to **organize your code** by splitting it into separate, reusable files. This makes large projects easier to maintain and understand.
## Exporting Items from a Module
In Rhai modules:
- **Functions are automatically exported** by default. You do **not** need to use `export` for functions.
- **Variables, constants, and objects must be exported manually** using the `export` keyword.
```js
// File: ./foo/baz.rhai
/// A function that is automatically exported
fn greet() {
return "Greetings!"
}
/// A private function, NOT exported automatically
private fn foo() {
return "This function is hidden and not exported"
}
/// A variable
let PI = 3.14159;
// Export the variable explicitly
export PI;
```
**Tip:** Only variables, constants, and objects require the `export` keyword. Functions are always available unless marked `private`. [More info](https://rhai.rs/book/language/modules/export.html#export-functions)
## Importing a Module
You can import a module using the `import` keyword:
```js
// File: ./ewwii.rhai
import "foo/baz" // just runs the script without importing it.
import "foo/baz" as example; // runs the script and imports it into example.
// Access exported items
print(example::greet()); // Greetings!
print(example::PI); // 3.14159
```
**Tip:** Always use the `as` keyword to import a script as a module with the name you desire.
## Notes
- Functions are automatically exported unless explicitly marked `private`.
- Variables, constants, and objects must be exported using the `export` keyword.
- `as` keyword is important in an `import` statement if you want to import the variables and functions in a Rhai file.

View File

@@ -1,15 +0,0 @@
# Styling Widgets
Ewwii also allows writing inline styles to widgets using the `style` property. These styles are then applied at runtime using GTK's CSS system.
**Example:**
```js
fn foo() {
return box(#{
style: "color: black; background-color: #fff;",
}, [ label(#{ text: "baz" }) ]);
}
```
> This example makes the text color of all child widgets of box to black and sets the background color of the box to white (`#fff` is the hexadecimal for white).

View File

@@ -1,11 +0,0 @@
# Theming & UI
This section focuses on visual styling and user interface customization. The core visual layer is built atop GTK, allowing deep integration with system themes while also enabling custom overrides through SCSS-style syntax.
We'll explore:
- How GTK theming works under the hood
- Styling your widgets using theme classes and custom CSS
- Layout implications of theme decisions (e.g., spacing, margins, z-order)
Whether you're trying to match your system's look or building a highly customized UI, this section gives you the tools to style confidently.

View File

@@ -1,27 +0,0 @@
# Working with GTK
## Gtk Theming
Ewwii is styled in GTK CSS.
You can use `Vanilla CSS` or `SCSS` to make theming even easier. The latter is compiled into CSS for you.
If you don't know any way to style something check out the [GTK CSS Overview wiki](https://docs.gtk.org/gtk3/css-overview.html),
the [GTK CSS Properties Overview wiki ](https://docs.gtk.org/gtk3/css-properties.html),
or use the [GTK-Debugger](#gtk-debugger).
If you have **NO** clue about how to do CSS, check out some online guides or tutorials.
SCSS is _very_ close to CSS, so if you know CSS you'll have no problem learning SCSS.
## GTK Debugger
The debugger can be used for **a lot** of things, especially if something doesn't work or isn't styled right.
To open the GTK debugger, simply run
```bash
ewwii inspector
```
If a style or something similar doesn't work, you can click on the icon in the top left to select the thing that isn't being styled correctly.
Then you can click on the drop down menu in the top right corner and select CSS Nodes. Here you will see everything about styling it, CSS Properties, and how it's structured.

View File

@@ -1,35 +0,0 @@
# Troubleshooting
Here you will find help if something doesn't work. If the issue isn't listed here, please [open an issue on the GitHub repo.](https://github.com/Ewwii-sh/ewwii/issues)
## Ewwii does not compile
1. Make sure that you are compiling ewwii using a recent version of rust (run `rustup update` to be sure you have the latest version available)
2. Make sure you have all the necessary dependencies. If there are compile-errors, the compiler will tell you what you're missing.
## Ewwii does not work on Wayland
1. Make sure you compiled ewwii with the `--no-default-features --features=wayland` flags.
2. Make sure that you're not trying to use X11-specific features (these are (hopefully) explicitly specified as such in the documentation).
## My configuration is not loaded correctly
1. Make sure the `ewwii.rhai` and `ewwii.(s)css` files are in the correct places.
2. Sometimes, ewwii might fail to load your configuration as a result of a configuration error. Make sure your configuration is valid.
## Something isn't styled correctly!
Check the [GTK-Debugger](working_with_gtk.md#gtk-debugger) to get more insight into what styles GTK is applying to which elements.
## General issues
You should try the following things before opening an issue or doing more specialized troubleshooting:
- Kill the ewwii daemon by running `ewwii kill` and re-open your window with the `--debug`-flag to get additional log output.
- Now you can take a look at the logs by running `ewwii logs`.
- Use `ewwii state` to see the state of all variables.
- Use `ewwii debug` to see the structure of your widget and other information.
- Update to the latest ewwii version.
- Sometimes hot reloading doesn't work. In that case, you can make use of `ewwii reload` manually.
Remember, if your issue isn't listed here, [open an issue on the GitHub repo](https://github.com/Ewwii-sh/ewwii/issues).

View File

@@ -1,254 +0,0 @@
# Widget Properties
## widget
These properties apply to all widgets, and can be used anywhere!
**Properties**
- `class`: `string` css class name
- `valign`: `string` how to align this vertically. possible values: "fill", "baseline", "center", "start", "end"
- `halign`: `string` how to align this horizontally. possible values: "fill", "baseline", "center", "start", "end"
- `vexpand`: `bool` should this container expand vertically. Default: false
- `hexpand`: `bool` should this widget expand horizontally. Default: false
- `width`: `int` width of this element
- `height`: `int` height of this element
- `active`: `bool` If this widget can be interacted with
- `tooltip`: `string` tooltip text (on hover)
- `visible`: `bool` visibility of the widget
- `style`: `string` inline scss style applied to the widget
- `css`: `string` scss code applied to the widget
## combo-box-text
**Properties**
- `items`: `vec` Items displayed in the combo box
- `timeout`: `duration` timeout of the command. Default: "200ms"
- `onchange`: `string` runs when an item is selected, replacing `{}` with the item
## expander
**Properties**
- `name`: `string` name of the expander
- `expanded`: `bool` sets whether it's expanded
## revealer
**Properties**
- `transition`: `string` animation name ("slideright", "slideleft", etc.)
- `reveal`: `bool` whether the child is revealed
- `duration`: `duration` how long the transition lasts. Default: "500ms"
## checkbox
**Properties**
- `checked`: `bool` initial checked state
- `timeout`: `duration` command timeout. Default: "200ms"
- `onchecked`: `string` command when checked
- `onunchecked`: `string` command when unchecked
## color-button
**Properties**
- `use_alpha`: `bool` use alpha channel
- `onchange`: `string` command on color select
- `timeout`: `duration` Default: "200ms"
## color-chooser
**Properties**
- `use_alpha`: `bool` use alpha channel
- `onchange`: `string` command on color select
- `timeout`: `duration` Default: "200ms"
## slider
**Properties**
- `flipped`: `bool` reverse direction
- `marks`: `string` draw marks
- `draw_value`: `bool` show value
- `value_pos`: `string` where to show value ("left", "right", etc.)
- `round_digits`: `int` number of decimal places
- `value`: `float` current value
- `min`: `float` minimum value
- `max`: `float` maximum value
- `timeout`: `duration` Default: "200ms"
- `onchange`: `string` command on change (use `{}` for value)
- `orientation`: `string` layout direction
## progress
**Properties**
- `flipped`: `bool` reverse direction
- `value`: `float` progress (0100)
- `orientation`: `string` layout direction
## input
**NOTE:** This widget exposes a special environment variable `INPUT_VAL` to the commands specified in `onchange` and `onaccept`.
**Properties**
- `value`: `string` current text
- `onchange`: `string` command on change; `INPUT_VAL` contains the new value
- `timeout`: `duration` Default: "200ms"
- `onaccept`: `string` command on Enter; `INPUT_VAL` contains the new value
- `password`: `bool` obscure input
## button
**Properties**
- `timeout`: `duration` Default: "200ms"
- `onclick`: `string` command on activation
- `onmiddleclick`: `string` command on middle click
- `onrightclick`: `string` command on right click
## image
**Properties**
- `path`: `string` image file path
- `image_width`: `int` image width
- `image_height`: `int` image height
- `preserve_aspect_ratio`: `bool` keep aspect ratio
- `fill_svg`: `string` fill color for SVGs
- `icon`: `string` theme icon name
- `icon_size`: `string` size of the icon
## box
**Properties**
- `spacing`: `int` spacing between children
- `orientation`: `string` direction of children
- `space_evenly`: `bool` distribute children evenly
## overlay
**Properties**
_None_
## tooltip
**Properties**
_None listed_
## centerbox
**Properties**
- `orientation`: `string` direction of layout
## scroll
**Properties**
- `hscroll`: `bool` allow horizontal scrolling
- `vscroll`: `bool` allow vertical scrolling
- `propagate_natural_height`: `bool` use the natural height if true
## eventbox
**Properties**
- `timeout`: `duration` Default: "200ms"
- `onscroll`: `string` command on scroll (`{}` becomes direction)
- `onhover`: `string` command on hover
- `onhoverlost`: `string` command on hover exit
- `cursor`: `string` cursor type
- `ondropped`: `string` command on drop (`{}` is URI)
- `dragvalue`: `string` URI to drag from this widget
- `dragtype`: `string` type to drag ("file", "text")
- `onclick`: `string` command on click
- `onmiddleclick`: `string` command on middle click
- `onrightclick`: `string` command on right click
## label
**Properties**
- `text`: `string` text to display
- `truncate`: `bool` truncate text
- `limit_width`: `int` max characters to show
- `truncate_left`: `bool` truncate beginning
- `show_truncated`: `bool` show truncation
- `unindent`: `bool` strip leading spaces
- `markup`: `string` Pango markup
- `wrap`: `bool` wrap text
- `angle`: `float` rotation angle
- `gravity`: `string` text gravity
- `xalign`: `float` horizontal alignment
- `yalign`: `float` vertical alignment
- `justify`: `string` text justification
- `wrap_mode`: `string` wrap mode ("word", "char", etc.)
- `lines`: `int` max lines (1 = unlimited)
## literal
**Properties**
- `content`: `string` raw yuck
## calendar
**Properties**
- `day`: `float` selected day
- `month`: `float` selected month
- `year`: `float` selected year
- `show_details`: `bool` show details
- `show_heading`: `bool` show heading
- `show_day_names`: `bool` show day names
- `show_week_numbers`: `bool` show week numbers
- `onclick`: `string` command with `{0}`, `{1}`, `{2}` for day/month/year
- `timeout`: `duration` Default: "200ms"
## stack
**Properties**
- `selected`: `int` child index
- `transition`: `string` animation name
- `same_size`: `bool` equal child size
## transform
**Properties**
- `rotate`: `float` rotation angle
- `transform_origin_x`: `string` transform origin x
- `transform_origin_y`: `string` transform origin y
- `translate_x`: `string` shift x
- `translate_y`: `string` shift y
- `scale-x`: `string` scale x
- `scale-y`: `string` scale y
## circular-progress
**Properties**
- `value`: `float` 0100 progress
- `start_at`: `float` start percentage
- `thickness`: `float` line thickness
- `clockwise`: `bool` direction
## graph
**Properties**
- `value`: `float` current value
- `thickness`: `float` line thickness
- `time_range`: `duration` duration to track
- `min`: `float` minimum value
- `max`: `float` maximum value

View File

@@ -1,12 +0,0 @@
# Widgets
Widgets are the building blocks of your interface. Each widget represents a visual element—text, images, containers, interactive components—that can be composed, styled, and updated dynamically.
This section introduces:
- The basic anatomy of a widget definition
- How widgets are laid out within windows
- The attributes and properties available to each widget type
- Patterns for building reusable or dynamic widget trees
Youll also learn how widget properties interact with the expression language and how reactivity is handled across updates.

View File

@@ -1,49 +0,0 @@
# Widgets & Parameters
Below is a list of available widgets and the parameters each accepts.
## Widget Functions
These functions correspond to actual GTK widgets and render visible UI elements.
- **box**: `props`, `children`
- **centerbox**: `props`, `children`
- **eventbox**: `props`, `children`
- **tooltip**: `props`, `children`
- **circular_progress**: `props`
- **graph**: `props`
- **transform**: `props`
- **slider**: `props`
- **progress**: `props`
- **image**: `props`
- **button**: `props`
- **label**: `props`
- **input**: `props`
- **calendar**: `props`
- **color_button**: `props`
- **expander**: `props`, `children`
- **color_chooser**: `props`
- **combo_box_text**: `props`
- **checkbox**: `props`
- **revealer**: `props`, `children`
- **scroll**: `props`, `children`
- **overlay**: `props`, `children`
- **stack**: `props`, `children`
## Utility Functions
These are not visible UI widgets but are essential for layout, data binding, or dynamic behavior.
- **defwindow**: `string`, `props`, `children`
- **poll**: `props`
- **listen**: `props`
> **Let's recall**
>
> ---
>
> `props` param: Defined in `#{}`
>
> `children` param: Defined in `[]`
>
> > Both of these are discussed in [chapter 2.2](../config/config_fundamentals.md)

View File

@@ -1,844 +0,0 @@
/* https://highlightjs.readthedocs.io/en/latest/css-classes-reference.html */
.latte.hljs {
color: #4c4f69;
background: #eff1f5;
}
.latte .hljs-keyword {
color: #8839ef;
}
.latte .hljs-built_in {
color: #d20f39;
}
.latte .hljs-type {
color: #df8e1d;
}
.latte .hljs-literal {
color: #fe640b;
}
.latte .hljs-number {
color: #fe640b;
}
.latte .hljs-operator {
color: #04a5e5;
}
.latte .hljs-punctuation {
color: #5c5f77;
}
.latte .hljs-property {
color: #179299;
}
.latte .hljs-regexp {
color: #ea76cb;
}
.latte .hljs-string {
color: #40a02b;
}
.latte .hljs-char.escape_ {
color: #40a02b;
}
.latte .hljs-subst {
color: #6c6f85;
}
.latte .hljs-symbol {
color: #dd7878;
}
.latte .hljs-variable {
color: #8839ef;
}
.latte .hljs-variable.language_ {
color: #8839ef;
}
.latte .hljs-variable.constant_ {
color: #fe640b;
}
.latte .hljs-title {
color: #1e66f5;
}
.latte .hljs-title.class_ {
color: #df8e1d;
}
.latte .hljs-title.function_ {
color: #1e66f5;
}
.latte .hljs-params {
color: #4c4f69;
}
.latte .hljs-comment {
color: #7c7f93;
}
.latte .hljs-doctag {
color: #d20f39;
}
.latte .hljs-meta {
color: #fe640b;
}
.latte .hljs-section {
color: #1e66f5;
}
.latte .hljs-tag {
color: #179299;
}
.latte .hljs-name {
color: #8839ef;
}
.latte .hljs-attr {
color: #1e66f5;
}
.latte .hljs-attribute {
color: #40a02b;
}
.latte .hljs-bullet {
color: #179299;
}
.latte .hljs-code {
color: #40a02b;
}
.latte .hljs-emphasis {
color: #d20f39;
font-style: italic;
}
.latte .hljs-strong {
color: #d20f39;
font-weight: bold;
}
.latte .hljs-formula {
color: #179299;
}
.latte .hljs-link {
color: #209fb5;
font-style: italic;
}
.latte .hljs-quote {
color: #40a02b;
font-style: italic;
}
.latte .hljs-selector-tag {
color: #df8e1d;
}
.latte .hljs-selector-id {
color: #1e66f5;
}
.latte .hljs-selector-class {
color: #179299;
}
.latte .hljs-selector-attr {
color: #8839ef;
}
.latte .hljs-selector-pseudo {
color: #179299;
}
.latte .hljs-template-tag {
color: #dd7878;
}
.latte .hljs-template-variable {
color: #dd7878;
}
.latte .hljs-addition {
color: #40a02b;
background: rgba(64, 160, 43, 0.15);
}
.latte .hljs-deletion {
color: #d20f39;
background: rgba(210, 15, 57, 0.15);
}
.latte :is(h1, h2, h3, h4, h5, h6) a code {
color: #4c4f69;
}
.latte a code {
color: #1e66f5;
}
.latte code {
color: #4c4f69;
background: #e6e9ef;
}
.latte blockquote blockquote {
border-top: 0.1em solid #acb0be;
border-bottom: 0.1em solid #acb0be;
}
.latte hr {
border-color: #acb0be;
border-style: solid;
}
.latte del {
color: #7c7f93;
}
.latte .ace_gutter {
color: #8c8fa1;
background: #e6e9ef;
}
.latte .ace_gutter-active-line.ace_gutter-cell {
color: #ea76cb;
background: #e6e9ef;
}
.latte .tooltiptext {
background: #e6e9ef;
color: #4c4f69;
}
.frappe.hljs {
color: #c6d0f5;
background: #303446;
}
.frappe .hljs-keyword {
color: #ca9ee6;
}
.frappe .hljs-built_in {
color: #e78284;
}
.frappe .hljs-type {
color: #e5c890;
}
.frappe .hljs-literal {
color: #ef9f76;
}
.frappe .hljs-number {
color: #ef9f76;
}
.frappe .hljs-operator {
color: #99d1db;
}
.frappe .hljs-punctuation {
color: #b5bfe2;
}
.frappe .hljs-property {
color: #81c8be;
}
.frappe .hljs-regexp {
color: #f4b8e4;
}
.frappe .hljs-string {
color: #a6d189;
}
.frappe .hljs-char.escape_ {
color: #a6d189;
}
.frappe .hljs-subst {
color: #a5adce;
}
.frappe .hljs-symbol {
color: #eebebe;
}
.frappe .hljs-variable {
color: #ca9ee6;
}
.frappe .hljs-variable.language_ {
color: #ca9ee6;
}
.frappe .hljs-variable.constant_ {
color: #ef9f76;
}
.frappe .hljs-title {
color: #8caaee;
}
.frappe .hljs-title.class_ {
color: #e5c890;
}
.frappe .hljs-title.function_ {
color: #8caaee;
}
.frappe .hljs-params {
color: #c6d0f5;
}
.frappe .hljs-comment {
color: #949cbb;
}
.frappe .hljs-doctag {
color: #e78284;
}
.frappe .hljs-meta {
color: #ef9f76;
}
.frappe .hljs-section {
color: #8caaee;
}
.frappe .hljs-tag {
color: #81c8be;
}
.frappe .hljs-name {
color: #ca9ee6;
}
.frappe .hljs-attr {
color: #8caaee;
}
.frappe .hljs-attribute {
color: #a6d189;
}
.frappe .hljs-bullet {
color: #81c8be;
}
.frappe .hljs-code {
color: #a6d189;
}
.frappe .hljs-emphasis {
color: #e78284;
font-style: italic;
}
.frappe .hljs-strong {
color: #e78284;
font-weight: bold;
}
.frappe .hljs-formula {
color: #81c8be;
}
.frappe .hljs-link {
color: #85c1dc;
font-style: italic;
}
.frappe .hljs-quote {
color: #a6d189;
font-style: italic;
}
.frappe .hljs-selector-tag {
color: #e5c890;
}
.frappe .hljs-selector-id {
color: #8caaee;
}
.frappe .hljs-selector-class {
color: #81c8be;
}
.frappe .hljs-selector-attr {
color: #ca9ee6;
}
.frappe .hljs-selector-pseudo {
color: #81c8be;
}
.frappe .hljs-template-tag {
color: #eebebe;
}
.frappe .hljs-template-variable {
color: #eebebe;
}
.frappe .hljs-addition {
color: #a6d189;
background: rgba(166, 209, 137, 0.15);
}
.frappe .hljs-deletion {
color: #e78284;
background: rgba(231, 130, 132, 0.15);
}
.frappe :is(h1, h2, h3, h4, h5, h6) a code {
color: #c6d0f5;
}
.frappe a code {
color: #8caaee;
}
.frappe code {
color: #c6d0f5;
background: #292c3c;
}
.frappe blockquote blockquote {
border-top: 0.1em solid #626880;
border-bottom: 0.1em solid #626880;
}
.frappe hr {
border-color: #626880;
border-style: solid;
}
.frappe del {
color: #949cbb;
}
.frappe .ace_gutter {
color: #838ba7;
background: #292c3c;
}
.frappe .ace_gutter-active-line.ace_gutter-cell {
color: #f4b8e4;
background: #292c3c;
}
.frappe .tooltiptext {
background: #292c3c;
color: #c6d0f5;
}
.macchiato.hljs {
color: #cad3f5;
background: #24273a;
}
.macchiato .hljs-keyword {
color: #c6a0f6;
}
.macchiato .hljs-built_in {
color: #ed8796;
}
.macchiato .hljs-type {
color: #eed49f;
}
.macchiato .hljs-literal {
color: #f5a97f;
}
.macchiato .hljs-number {
color: #f5a97f;
}
.macchiato .hljs-operator {
color: #91d7e3;
}
.macchiato .hljs-punctuation {
color: #b8c0e0;
}
.macchiato .hljs-property {
color: #8bd5ca;
}
.macchiato .hljs-regexp {
color: #f5bde6;
}
.macchiato .hljs-string {
color: #a6da95;
}
.macchiato .hljs-char.escape_ {
color: #a6da95;
}
.macchiato .hljs-subst {
color: #a5adcb;
}
.macchiato .hljs-symbol {
color: #f0c6c6;
}
.macchiato .hljs-variable {
color: #c6a0f6;
}
.macchiato .hljs-variable.language_ {
color: #c6a0f6;
}
.macchiato .hljs-variable.constant_ {
color: #f5a97f;
}
.macchiato .hljs-title {
color: #8aadf4;
}
.macchiato .hljs-title.class_ {
color: #eed49f;
}
.macchiato .hljs-title.function_ {
color: #8aadf4;
}
.macchiato .hljs-params {
color: #cad3f5;
}
.macchiato .hljs-comment {
color: #939ab7;
}
.macchiato .hljs-doctag {
color: #ed8796;
}
.macchiato .hljs-meta {
color: #f5a97f;
}
.macchiato .hljs-section {
color: #8aadf4;
}
.macchiato .hljs-tag {
color: #8bd5ca;
}
.macchiato .hljs-name {
color: #c6a0f6;
}
.macchiato .hljs-attr {
color: #8aadf4;
}
.macchiato .hljs-attribute {
color: #a6da95;
}
.macchiato .hljs-bullet {
color: #8bd5ca;
}
.macchiato .hljs-code {
color: #a6da95;
}
.macchiato .hljs-emphasis {
color: #ed8796;
font-style: italic;
}
.macchiato .hljs-strong {
color: #ed8796;
font-weight: bold;
}
.macchiato .hljs-formula {
color: #8bd5ca;
}
.macchiato .hljs-link {
color: #7dc4e4;
font-style: italic;
}
.macchiato .hljs-quote {
color: #a6da95;
font-style: italic;
}
.macchiato .hljs-selector-tag {
color: #eed49f;
}
.macchiato .hljs-selector-id {
color: #8aadf4;
}
.macchiato .hljs-selector-class {
color: #8bd5ca;
}
.macchiato .hljs-selector-attr {
color: #c6a0f6;
}
.macchiato .hljs-selector-pseudo {
color: #8bd5ca;
}
.macchiato .hljs-template-tag {
color: #f0c6c6;
}
.macchiato .hljs-template-variable {
color: #f0c6c6;
}
.macchiato .hljs-addition {
color: #a6da95;
background: rgba(166, 218, 149, 0.15);
}
.macchiato .hljs-deletion {
color: #ed8796;
background: rgba(237, 135, 150, 0.15);
}
.macchiato :is(h1, h2, h3, h4, h5, h6) a code {
color: #cad3f5;
}
.macchiato a code {
color: #8aadf4;
}
.macchiato code {
color: #cad3f5;
background: #1e2030;
}
.macchiato blockquote blockquote {
border-top: 0.1em solid #5b6078;
border-bottom: 0.1em solid #5b6078;
}
.macchiato hr {
border-color: #5b6078;
border-style: solid;
}
.macchiato del {
color: #939ab7;
}
.macchiato .ace_gutter {
color: #8087a2;
background: #1e2030;
}
.macchiato .ace_gutter-active-line.ace_gutter-cell {
color: #f5bde6;
background: #1e2030;
}
.macchiato .tooltiptext {
background: #1e2030;
color: #cad3f5;
}
.mocha.hljs {
color: #cdd6f4;
background: #1e1e2e;
}
.mocha .hljs-keyword {
color: #cba6f7;
}
.mocha .hljs-built_in {
color: #f38ba8;
}
.mocha .hljs-type {
color: #f9e2af;
}
.mocha .hljs-literal {
color: #fab387;
}
.mocha .hljs-number {
color: #fab387;
}
.mocha .hljs-operator {
color: #89dceb;
}
.mocha .hljs-punctuation {
color: #bac2de;
}
.mocha .hljs-property {
color: #94e2d5;
}
.mocha .hljs-regexp {
color: #f5c2e7;
}
.mocha .hljs-string {
color: #a6e3a1;
}
.mocha .hljs-char.escape_ {
color: #a6e3a1;
}
.mocha .hljs-subst {
color: #a6adc8;
}
.mocha .hljs-symbol {
color: #f2cdcd;
}
.mocha .hljs-variable {
color: #cba6f7;
}
.mocha .hljs-variable.language_ {
color: #cba6f7;
}
.mocha .hljs-variable.constant_ {
color: #fab387;
}
.mocha .hljs-title {
color: #89b4fa;
}
.mocha .hljs-title.class_ {
color: #f9e2af;
}
.mocha .hljs-title.function_ {
color: #89b4fa;
}
.mocha .hljs-params {
color: #cdd6f4;
}
.mocha .hljs-comment {
color: #9399b2;
}
.mocha .hljs-doctag {
color: #f38ba8;
}
.mocha .hljs-meta {
color: #fab387;
}
.mocha .hljs-section {
color: #89b4fa;
}
.mocha .hljs-tag {
color: #94e2d5;
}
.mocha .hljs-name {
color: #cba6f7;
}
.mocha .hljs-attr {
color: #89b4fa;
}
.mocha .hljs-attribute {
color: #a6e3a1;
}
.mocha .hljs-bullet {
color: #94e2d5;
}
.mocha .hljs-code {
color: #a6e3a1;
}
.mocha .hljs-emphasis {
color: #f38ba8;
font-style: italic;
}
.mocha .hljs-strong {
color: #f38ba8;
font-weight: bold;
}
.mocha .hljs-formula {
color: #94e2d5;
}
.mocha .hljs-link {
color: #74c7ec;
font-style: italic;
}
.mocha .hljs-quote {
color: #a6e3a1;
font-style: italic;
}
.mocha .hljs-selector-tag {
color: #f9e2af;
}
.mocha .hljs-selector-id {
color: #89b4fa;
}
.mocha .hljs-selector-class {
color: #94e2d5;
}
.mocha .hljs-selector-attr {
color: #cba6f7;
}
.mocha .hljs-selector-pseudo {
color: #94e2d5;
}
.mocha .hljs-template-tag {
color: #f2cdcd;
}
.mocha .hljs-template-variable {
color: #f2cdcd;
}
.mocha .hljs-addition {
color: #a6e3a1;
background: rgba(166, 227, 161, 0.15);
}
.mocha .hljs-deletion {
color: #f38ba8;
background: rgba(243, 139, 168, 0.15);
}
.mocha :is(h1, h2, h3, h4, h5, h6) a code {
color: #cdd6f4;
}
.mocha a code {
color: #89b4fa;
}
.mocha code {
color: #cdd6f4;
background: #181825;
}
.mocha blockquote blockquote {
border-top: 0.1em solid #585b70;
border-bottom: 0.1em solid #585b70;
}
.mocha hr {
border-color: #585b70;
border-style: solid;
}
.mocha del {
color: #9399b2;
}
.mocha .ace_gutter {
color: #7f849c;
background: #181825;
}
.mocha .ace_gutter-active-line.ace_gutter-cell {
color: #f5c2e7;
background: #181825;
}
.mocha .tooltiptext {
background: #181825;
color: #cdd6f4;
}
.latte {
--bg: #eff1f5;
--fg: #4c4f69;
--sidebar-bg: #e6e9ef;
--sidebar-fg: #4c4f69;
--sidebar-non-existant: #9ca0b0;
--sidebar-active: #1e66f5;
--sidebar-spacer: #9ca0b0;
--scrollbar: #9ca0b0;
--icons: #9ca0b0;
--icons-hover: #7c7f93;
--links: #1e66f5;
--inline-code-color: #4c4f69;
--theme-popup-bg: #e6e9ef;
--theme-popup-border: #9ca0b0;
--theme-hover: #ccd0da;
--quote-bg: #e6e9ef;
--quote-border: #dce0e8;
--table-border-color: #ccd0da;
--table-header-bg: #e6e9ef;
--table-alternate-bg: #e6e9ef;
--searchbar-border-color: #ccd0da;
--searchbar-bg: #e6e9ef;
--searchbar-fg: #4c4f69;
--searchbar-shadow-color: #dce0e8;
--searchresults-header-fg: #4c4f69;
--searchresults-border-color: #ccd0da;
--searchresults-li-bg: #eff1f5;
--search-mark-bg: #fe640b;
--warning-border: #fe640b;
--color-scheme: light;
--copy-button-filter: brightness(0) saturate(100%) invert(47%) sepia(6%) saturate(1263%) hue-rotate(195deg) brightness(90%) contrast(81%);
--copy-button-filter-hover: brightness(0) saturate(100%) invert(30%) sepia(80%) saturate(1850%) hue-rotate(209deg) brightness(94%) contrast(105%);
}
.frappe {
--bg: #303446;
--fg: #c6d0f5;
--sidebar-bg: #292c3c;
--sidebar-fg: #c6d0f5;
--sidebar-non-existant: #737994;
--sidebar-active: #8caaee;
--sidebar-spacer: #737994;
--scrollbar: #737994;
--icons: #737994;
--icons-hover: #949cbb;
--links: #8caaee;
--inline-code-color: #c6d0f5;
--theme-popup-bg: #292c3c;
--theme-popup-border: #737994;
--theme-hover: #414559;
--quote-bg: #292c3c;
--quote-border: #232634;
--table-border-color: #414559;
--table-header-bg: #292c3c;
--table-alternate-bg: #292c3c;
--searchbar-border-color: #414559;
--searchbar-bg: #292c3c;
--searchbar-fg: #c6d0f5;
--searchbar-shadow-color: #232634;
--searchresults-header-fg: #c6d0f5;
--searchresults-border-color: #414559;
--searchresults-li-bg: #303446;
--search-mark-bg: #ef9f76;
--warning-border: #ef9f76;
--color-scheme: dark;
--copy-button-filter: brightness(0) saturate(100%) invert(82%) sepia(6%) saturate(1287%) hue-rotate(192deg) brightness(86%) contrast(85%);
--copy-button-filter-hover: brightness(0) saturate(100%) invert(68%) sepia(16%) saturate(1070%) hue-rotate(185deg) brightness(96%) contrast(95%);
}
.macchiato {
--bg: #24273a;
--fg: #cad3f5;
--sidebar-bg: #1e2030;
--sidebar-fg: #cad3f5;
--sidebar-non-existant: #6e738d;
--sidebar-active: #8aadf4;
--sidebar-spacer: #6e738d;
--scrollbar: #6e738d;
--icons: #6e738d;
--icons-hover: #939ab7;
--links: #8aadf4;
--inline-code-color: #cad3f5;
--theme-popup-bg: #1e2030;
--theme-popup-border: #6e738d;
--theme-hover: #363a4f;
--quote-bg: #1e2030;
--quote-border: #181926;
--table-border-color: #363a4f;
--table-header-bg: #1e2030;
--table-alternate-bg: #1e2030;
--searchbar-border-color: #363a4f;
--searchbar-bg: #1e2030;
--searchbar-fg: #cad3f5;
--searchbar-shadow-color: #181926;
--searchresults-header-fg: #cad3f5;
--searchresults-border-color: #363a4f;
--searchresults-li-bg: #24273a;
--search-mark-bg: #f5a97f;
--warning-border: #f5a97f;
--color-scheme: dark;
--copy-button-filter: brightness(0) saturate(100%) invert(75%) sepia(18%) saturate(361%) hue-rotate(190deg) brightness(91%) contrast(86%);
--copy-button-filter-hover: brightness(0) saturate(100%) invert(67%) sepia(17%) saturate(1007%) hue-rotate(183deg) brightness(99%) contrast(94%);
}
.mocha {
--bg: #1e1e2e;
--fg: #cdd6f4;
--sidebar-bg: #181825;
--sidebar-fg: #cdd6f4;
--sidebar-non-existant: #6c7086;
--sidebar-active: #89b4fa;
--sidebar-spacer: #6c7086;
--scrollbar: #6c7086;
--icons: #6c7086;
--icons-hover: #9399b2;
--links: #89b4fa;
--inline-code-color: #cdd6f4;
--theme-popup-bg: #181825;
--theme-popup-border: #6c7086;
--theme-hover: #313244;
--quote-bg: #181825;
--quote-border: #11111b;
--table-border-color: #313244;
--table-header-bg: #181825;
--table-alternate-bg: #181825;
--searchbar-border-color: #313244;
--searchbar-bg: #181825;
--searchbar-fg: #cdd6f4;
--searchbar-shadow-color: #11111b;
--searchresults-header-fg: #cdd6f4;
--searchresults-border-color: #313244;
--searchresults-li-bg: #1e1e2e;
--search-mark-bg: #fab387;
--warning-border: #fab387;
--color-scheme: dark;
--copy-button-filter: brightness(0) saturate(100%) invert(84%) sepia(9%) saturate(767%) hue-rotate(192deg) brightness(84%) contrast(84%);
--copy-button-filter-hover: brightness(0) saturate(100%) invert(68%) sepia(18%) saturate(951%) hue-rotate(180deg) brightness(98%) contrast(100%);
}

Some files were not shown because too many files have changed in this diff Show More