commit b203b6de5283b426c2463510f129cdc2914daa7a Author: Javier Feliz Date: Wed Sep 3 00:20:33 2025 -0400 WIP diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..65326bb --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..725d305 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,85 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Waycast is a GTK4-based application launcher for Wayland compositors, built with Rust using the relm4 framework. It provides a floating launcher interface that displays desktop applications with icons and allows users to search and launch them. + +## Build and Development Commands + +```bash +# Build the project +cargo build + +# Run the application +cargo run + +# Build for release +cargo build --release + +# Check code formatting +cargo fmt --check + +# Format code +cargo fmt + +# Run clippy lints +cargo clippy + +# Run tests (if any exist) +cargo test +``` + +## Architecture + +### Core Components + +- **main.rs**: Contains the main GTK4/relm4 application with two primary components: + - `AppModel`: Main window component with search functionality + - `ListItem`: Factory component for rendering individual launcher items + +- **lib.rs**: Defines core traits and types: + - `LauncherListItem` trait: Interface for launchable items + - `LaunchError` enum: Error handling for launch operations + +- **drun module** (`src/drun/mod.rs`): Handles desktop application discovery + - `DesktopEntry` struct: Represents a .desktop file + - `all()` function: Scans XDG_DATA_DIRS for desktop applications + - Implements `LauncherListItem` trait for desktop entries + +- **util module** (`src/util/`): Utility functions + - `files.rs`: File system operations, particularly `get_files_with_extension()` + +### Key Technologies + +- **GTK4**: UI framework with gtk4-layer-shell for Wayland layer shell protocol +- **relm4**: Reactive UI framework for GTK4 applications +- **gio**: GLib I/O library for desktop app info and icon handling + +### Important Implementation Details + +- Uses gtk4-layer-shell to create a floating overlay window on Wayland +- Desktop applications are discovered by parsing .desktop files from XDG_DATA_DIRS +- Icons are handled through GIO's Icon system (ThemedIcon and FileIcon) +- Factory pattern is used for efficiently rendering lists of launcher items + +### Lifetime Management + +When working with GTK widgets in relm4 view macros, be careful with string references. The view macro context has specific lifetime requirements: +- Avoid returning `&str` from methods called in view macros +- Use `self.field.as_ref().map(|s| s.as_str())` pattern for Option to Option<&str> conversion +- Static strings work fine, but dynamic references may cause stack overflows + +### Module Structure + +``` +src/ +├── main.rs # Main application and UI components +├── lib.rs # Core traits and error types +├── drun/ +│ └── mod.rs # Desktop application discovery +└── util/ + ├── mod.rs # Utility module exports + └── files.rs # File system utilities +``` \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..35df3f8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1235 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "bitflags" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cairo-rs" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1158f326d7b755a9ae2b36c5b5391400e3431f3b77418cedb6d7130126628f10" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b963177900ec8e783927e5ed99e16c0ec1b723f1f125dff8992db28ef35c62c3" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cfg-expr" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d458d63f0f0f482c8da9b7c8b76c21bd885a02056cc94c6404d861ca2b8206" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7330cdbbc653df431331ae3d9d59e985a0fecaf33d74c7c1c5d13ab0245f6c" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25899cc931dc28cba912ebec793b730f53d2d419f90a562fcb29b53bd10aa82" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk4" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a67b064d2f35e649232455c7724f56f977555d2608c43300eabc530eaa4e359" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "gl", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2edbda0d879eb85317bdb49a3da591ed70a804a10776e358ef416be38c6db2c5" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gio" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5e3f390d01b79e30da451dd00e27cd1ac2de81658e3abf6c1fc3229b24c5f" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "pin-project-lite", + "smallvec", +] + +[[package]] +name = "gio-sys" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a03f2234671e5a588cfe1f59c2b22c103f5772ea351be9cc824a9ce0d06d99fd" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.60.2", +] + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glib" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60bdc26493257b5794ba9301f7cbaf7ab0d69a570bfbefa4d7d360e781cb5205" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e772291ebea14c28eb11bb75741f62f4a4894f25e60ce80100797b6b010ef0f9" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc7c43cff6a7dc43821e45ebf172399437acd6716fa2186b6852d2b397bf622d" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9a190eef2bce144a6aa8434e306974c6062c398e0a33a146d60238f9062d5c" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "graphene-rs" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96914394464c04df8279c23976293afd53b2588e03c9d8d9662ef6528654a85" +dependencies = [ + "glib", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8205bb19b7a041cf059be3c94d6b23b3f2c6c96362c44311dcf184e4a9422a" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gsk4" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5dbe33ceed6fc20def67c03d36e532f5a4a569ae437ae015a7146094f31e10c" +dependencies = [ + "cairo-rs", + "gdk4", + "glib", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d76011d55dd19fde16ffdedee08877ae6ec942818cfa7bc08a91259bc0b9fc9" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk4" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938d68ad43080ad5ee710c30d467c1bc022ee5947856f593855691d726305b3e" +dependencies = [ + "cairo-rs", + "field-offset", + "futures-channel", + "gdk-pixbuf", + "gdk4", + "gio", + "glib", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gtk4-layer-shell" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f448a3a5593fb2ba018a946474beec09366c01f40ec788fc4a879981d16c91" +dependencies = [ + "bitflags", + "gdk4", + "glib", + "glib-sys", + "gtk4", + "gtk4-layer-shell-sys", + "libc", +] + +[[package]] +name = "gtk4-layer-shell-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e5228548ca4610a3406ddd97eb1b3ad366de04d02ae6a8fa80eaad85be1931" +dependencies = [ + "gdk4-sys", + "glib-sys", + "gtk4-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk4-macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0912d2068695633002b92c5966edc108b2e4f54b58c509d1eeddd4cbceb7315c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gtk4-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a923bdcf00e46723801162de24432cbce38a6810e0178a2d0b6dd4ecc26a1c74" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "gsk4-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.59.0", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pango" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab47feb3403aa564edaeb68620c5b9159f8814733a7dd45f0b1a27d19de362fe" +dependencies = [ + "gio", + "glib", + "libc", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f855bccb447644e149fae79086e1f81514c30fe5e9b8bd257d9d3c941116c86" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "relm4" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bae902de22fd92e62641f047975abf228573425b9b8de175e8ab5b6cda10379" +dependencies = [ + "flume", + "fragile", + "futures", + "gtk4", + "once_cell", + "relm4-css", + "relm4-macros", + "tokio", + "tracing", +] + +[[package]] +name = "relm4-components" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3155d84425d33cdc62929ed2c79bfcfdcb2fff6340c5f7041ed24b56577c9d57" +dependencies = [ + "once_cell", + "relm4", + "tracker", +] + +[[package]] +name = "relm4-css" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37dbe7a114855a22618f0e13595ce6b3f165478c13c2dfc4f4f99614da105797" + +[[package]] +name = "relm4-macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175fce497fc6f11dde7ea56daa30ff7ad29a534bbc209d59d766659c880ba5f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracker" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5c98457ff700aaeefcd4a4a492096e78a2af1dd8523c66e94a3adb0fdbd415" +dependencies = [ + "tracker-macros", +] + +[[package]] +name = "tracker-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc19eb2373ccf3d1999967c26c3d44534ff71ae5d8b9dacf78f4b13132229e48" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "waycast" +version = "0.1.0" +dependencies = [ + "gio", + "glib", + "gtk4", + "gtk4-layer-shell", + "relm4", + "relm4-components", + "tracker", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "xml-rs" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..57f4fa4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "waycast" +version = "0.1.0" +edition = "2024" + +[dependencies] +gio = "0.21.1" +glib = "0.21.1" +gtk = { version = "0.10.0", package = "gtk4" } +gtk4-layer-shell = "0.6.1" +relm4 = "0.10.0" +relm4-components = "0.10.0" +tracker = "0.2.2" diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..bbfffcd --- /dev/null +++ b/shell.nix @@ -0,0 +1,37 @@ +# shell.nix +{ + pkgs ? import { }, +}: + +pkgs.mkShell { + # Tools you’ll use directly + buildInputs = [ + pkgs.pkg-config + + # GTK4 stack + pkgs.gtk4 + pkgs.glib + pkgs.gdk-pixbuf + pkgs.pango + pkgs.cairo + pkgs.harfbuzz + + # Wayland + layer shell (GTK4 variant) + pkgs.wayland + pkgs.gtk4-layer-shell + + # Icons (so themed icons resolve; harmless even if you don't use yet) + pkgs.hicolor-icon-theme + pkgs.adwaita-icon-theme + ]; + + shellHook = '' + export GDK_BACKEND=wayland + echo "gtk4: $(pkg-config --modversion gtk4 2>/dev/null || echo missing)" + echo "gtk4-layer-shell: $(pkg-config --modversion gtk4-layer-shell-0 2>/dev/null || echo missing)" + echo "wayland-client: $(pkg-config --modversion wayland-client 2>/dev/null || echo missing)" + echo "XDG_SESSION_TYPE=$XDG_SESSION_TYPE" + echo "WAYLAND_DISPLAY=$WAYLAND_DISPLAY" + echo "GDK_BACKEND=$GDK_BACKEND" + ''; +} diff --git a/src/drun/mod.rs b/src/drun/mod.rs new file mode 100644 index 0000000..65c14a2 --- /dev/null +++ b/src/drun/mod.rs @@ -0,0 +1,118 @@ +use crate::util::files; +use crate::{LaunchError, LauncherListItem}; +use gio::{AppInfo, DesktopAppInfo, Icon, prelude::*}; +use std::env; +use std::path::{Path, PathBuf}; + +#[derive(Debug)] +struct DesktopEntry { + id: String, + name: String, + generic_name: Option, + description: Option, + icon: Option, + exec: Option, + path: PathBuf, + no_display: bool, + is_terminal_app: bool, +} + +impl LauncherListItem for DesktopEntry { + fn title(&self) -> String { + return self.name.to_owned(); + } + + fn description(&self) -> Option { + if let Some(glib_string) = &self.description { + return Some(glib_string.to_string().to_owned()); + } + + return None; + } + + fn execute(&self) -> Result<(), LaunchError> { + if let Some(di) = DesktopAppInfo::from_filename(&self.path) { + let app: AppInfo = di.upcast(); + let ctx = gio::AppLaunchContext::new(); + if app.launch(&[], Some(&ctx)).ok().is_none() { + return Err(LaunchError::CouldNotLaunch("App failed to launch".into())); + }; + return Ok(()); + } + + return Err(LaunchError::CouldNotLaunch("Invalid .desktop entry".into())); + } + + fn icon(&self) -> String { + if let Some(icon) = &self.icon { + if let Ok(ti) = icon.clone().downcast::() { + // ThemedIcon may have multiple names, we take the first + if let Some(name) = ti.names().first() { + return name.to_string(); + } + } + + if let Ok(fi) = icon.clone().downcast::() { + if let Some(path) = fi.file().path() { + return path.to_string_lossy().to_string(); + } + } + } + + return "application-x-executable".into(); + } +} + +fn get_desktop_files() -> Vec { + let dir_envs = + env::var("XDG_DATA_DIRS").expect("XDG_DATA_DIRS not set. Please fix your environment"); + let dir_string = String::from(dir_envs); + let dirs = dir_string.split(":"); + + let mut files = Vec::new(); + for dir in dirs { + // println!("Data dir: {}", dir); + let apps_path = Path::new(dir).join("applications"); + let desktop_files = match files::get_files_with_extension(&apps_path, "desktop") { + Ok(files) => files, + Err(_) => { + // eprintln!("Error reading {dir}: {err}"); + continue; + } + }; + + for f in desktop_files { + files.push(f); + } + } + + return files; +} + +pub fn all() -> Vec> { + let mut entries: Vec> = Vec::new(); + + for f in get_desktop_files() { + if let Some(info) = DesktopAppInfo::from_filename(&f) { + if info.is_nodisplay() { + continue; + } + + let de = DesktopEntry { + id: info.id().unwrap_or_default().to_string(), + name: info.name().to_string(), + generic_name: info.generic_name(), + description: info.description(), + icon: info.icon(), + exec: info.string("Exec"), + no_display: info.is_nodisplay(), + path: f.clone(), + is_terminal_app: info.boolean("Terminal"), + }; + + entries.push(Box::new(de)); + } + } + + entries +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6cf3567 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,12 @@ +pub mod drun; +pub mod util; +pub enum LaunchError { + CouldNotLaunch(String), +} + +pub trait LauncherListItem { + fn title(&self) -> String; + fn description(&self) -> Option; + fn execute(&self) -> Result<(), LaunchError>; + fn icon(&self) -> String; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1f6564f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,168 @@ +use gio::{Icon, prelude::*}; +use gtk::{ + Box as GtkBox, CssProvider, Entry, IconLookupFlags, IconTheme, Image, Label, ListBox, ListView, + NoSelection, Orientation, STYLE_PROVIDER_PRIORITY_APPLICATION, ScrolledWindow, + SignalListItemFactory, StringList, StringObject, Window, +}; +use gtk::{Button, prelude::*}; +use gtk4_layer_shell as layerShell; +use layerShell::LayerShell; +use relm4::factory::{FactoryComponent, FactorySender, FactoryVecDeque}; +use relm4::{Component, ComponentParts, RelmApp, RelmWidgetExt, SimpleComponent, component}; +use waycast::{LaunchError, LauncherListItem, drun}; + +struct AppModel { + list_items: FactoryVecDeque, +} + +#[derive(Debug)] +enum AppMsg { + TextEntered(String), + ListItemSelected(String), + None, +} + +#[derive(Debug)] +struct ListItem { + text: String, + icon: String, +} + +#[relm4::factory] +impl FactoryComponent for ListItem { + type Init = (String, String); + type Input = (); + type Output = (); + type CommandOutput = (); + type ParentWidget = gtk::ListBox; + + view! { + #[root] + GtkBox { + set_orientation: Orientation::Horizontal, + set_spacing: 10, + + Image { + set_pixel_size: 50, + set_icon_name: Some(self.icon.as_str()), + }, + + Label { + set_xalign: 0.0, + set_label: &self.text + }, + } + } + + fn init_model( + (text, icon): Self::Init, + _index: &Self::Index, + _sender: FactorySender, + ) -> Self { + Self { text, icon } + } +} + +#[relm4::component] +impl SimpleComponent for AppModel { + type Init = StringList; + type Input = AppMsg; + type Output = (); + + view! { + #[name = "launcher_window"] + Window { + set_title: Some("Waycast"), + set_default_width: 800, + set_default_height: 500, + set_resizable: false, + + GtkBox { + set_orientation: Orientation::Vertical, + + #[name = "search_input"] + Entry { + set_placeholder_text: Some("Search..."), + connect_changed[sender] => move |e| { + sender.input(AppMsg::TextEntered(e.text().to_string())); + } + }, + + ScrolledWindow { + set_min_content_height: 300, + + #[local_ref] + items -> ListBox { + set_vexpand: true, + } + } + } + } + } + + fn init( + _list_items: Self::Init, + root: Self::Root, + sender: relm4::ComponentSender, + ) -> relm4::ComponentParts { + let mut list_items: FactoryVecDeque = FactoryVecDeque::builder() + .launch(ListBox::default()) + .forward(sender.input_sender(), |_| AppMsg::None); + { + let mut guard = list_items.guard(); + println!("Starting to load desktop entries..."); + let entries = drun::all(); + println!("Found {} entries", entries.len()); + for p in entries { + guard.push_back((p.title(), p.icon())); + } + println!("Finished loading entries"); + } + let model = AppModel { list_items }; + let items = model.list_items.widget(); + let widgets = view_output!(); + // Set up layer shell so the launcher can float + // like it's supposed to. + widgets.launcher_window.init_layer_shell(); + let edges = [ + layerShell::Edge::Top, + layerShell::Edge::Bottom, + layerShell::Edge::Left, + layerShell::Edge::Right, + ]; + for edge in edges { + widgets.launcher_window.set_anchor(edge, false); + } + widgets + .launcher_window + .set_keyboard_mode(layerShell::KeyboardMode::OnDemand); + widgets.launcher_window.set_layer(layerShell::Layer::Top); + ComponentParts { model, widgets } + } + + fn update(&mut self, message: Self::Input, _sender: relm4::ComponentSender) { + match message { + AppMsg::TextEntered(query) => { + println!("query: {query}"); + } + _ => unimplemented!(), + } + } +} + +macro_rules! yesno { + ($var:expr) => { + if $var { "Yes" } else { "No" } + }; +} + +fn main() { + let app = RelmApp::new("dev.thegrind.waycast"); + app.run::(StringList::new(&[])); + // let entries = drun::all(); + // for e in &entries { + // println!("---------------------"); + // println!("App: {}", e.title()); + // println!("Icon: {}", e.icon().unwrap_or("".into())); + // } +} diff --git a/src/util/files.rs b/src/util/files.rs new file mode 100644 index 0000000..994363b --- /dev/null +++ b/src/util/files.rs @@ -0,0 +1,26 @@ +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +pub fn get_files_with_extension>( + dir: P, + extension: &str, +) -> io::Result> { + let entries = fs::read_dir(dir)?; + let desktop_files: Vec<_> = entries + .filter_map(|res| res.ok()) + .map(|f| f.path()) + .filter(|path| { + path.extension() + .and_then(|ext| ext.to_str()) + .map(|ext| ext == extension) + .unwrap_or(false) + }) + .collect(); + + let mut files = Vec::new(); + for f in desktop_files { + files.push(f); + } + + return Ok(files); +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..d3ab969 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1 @@ +pub mod files;