Skip to content

Commit

Permalink
added styling
Browse files Browse the repository at this point in the history
Signed-off-by: Enrico Stemmer <stemmer.enrico@gmail.com>
  • Loading branch information
H3rmt committed Feb 2, 2024
1 parent 68c80af commit 66a8a88
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 66 deletions.
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ categories = ["command-line-utilities"]
[dependencies]
clap = { version = "^4.4.11", features = ["derive"] }
hyprland = "=0.3.11"
gtk4 = { version = "0.7.3", optional = true }
gtk4 = { version = "0.7.3", optional = true, features = ["v4_12"] }
libadwaita = { version = "0.5.3", optional = true }
gtk4-layer-shell = { version = "0.2.0", optional = true }
notify-rust = { version = "4.0.0", optional = true }
Expand All @@ -26,7 +26,8 @@ random_color = "0.8.0"
svg = "0.15.0"

[features]
default = ["gui", "daemon"]
gui = ["gtk4", "libadwaita", "gtk4-layer-shell", "tokio", "tokio-condvar", "icon-loader", "daemon"]
default = ["gui", "daemon", "adwaita"]
gui = ["gtk4", "gtk4-layer-shell", "tokio", "tokio-condvar", "icon-loader", "daemon"]
adwaita = ["libadwaita"]
daemon = ["tokio"]
toast = ["notify-rust"]
1 change: 1 addition & 0 deletions src/daemon.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::future::Future;
use std::path::Path;

use hyprland::data::Client;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{UnixListener, UnixStream};

Expand Down
133 changes: 90 additions & 43 deletions src/gui.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,85 @@
use gtk4::{ApplicationWindow, Frame, gdk, glib};
#[cfg(not(feature = "adwaita"))]
use gtk4::Application;
use gtk4::gdk::Monitor;
use gtk4::prelude::*;
use gtk4_layer_shell::{Layer, LayerShell};
use hyprland::data::Client;
use libadwaita as adw;
#[cfg(feature = "adwaita")]
use libadwaita::Application;
use tokio::sync::MutexGuard;

use crate::{Data, Info, Share};

const SIZE_FACTOR: i16 = 7;
const CSS: &str = r#"
frame.active {
background-color: rgba(0, 0, 0, 0.5);
}
frame.active-ws {
background-color: rgba(0, 0, 0, 0.2);
}
frame {
border-radius: 10px;
border: 3px solid rgba(0, 0, 0, 0.4);
}
window {
border-radius: 15px;
border: 6px solid rgba(0, 0, 0, 0.4);
}
frame.client:hover {
background-color: rgba(70, 70, 70, 0.2);
}
"#;

fn client_ui(client: &Client) -> Frame {
fn client_ui(client: &Client, active: bool) -> Frame {
let icon = gtk4::Image::from_icon_name(&client.class);
let pixel_size = (client.size.1 / (SIZE_FACTOR * 2)) as i32;
println!("pixel_size: {}", pixel_size);
icon.set_pixel_size(pixel_size);

let frame = Frame::builder()
.width_request((client.size.0 / SIZE_FACTOR) as i32)
.height_request((client.size.1 / SIZE_FACTOR) as i32)
.label(&client.class)
.label_xalign(0.5)
.css_classes(vec!["client"])
.child(&icon)
.build();


// let path = icon_loader::icon_loader_hicolor().load_icon(&client.class);
// println!("path: {:?}", path);
// if let Some(path) = path {
// let pixbuf = gdk_pixbuf::Pixbuf::from_file(path.file_for_size(64)).unwrap();
// let icon = gtk4::Image::from_pixbuf(Some(&pixbuf));
// frame.set_child(Some(&icon));
// } else {
// let icon = gtk4::Image::from_icon_name(&client.class);
// frame.set_child(Some(&icon));
// }
if active {
frame.add_css_class("active");
}

frame
}

fn activate(
app: &adw::Application,
focus: impl FnOnce(Client) + Copy + Send + 'static,
app: &Application,
monitor: &Monitor,
data: Share,
#[cfg(feature = "toast")]
do_toast: bool,
) {
let workspaces_box = gtk4::Box::builder()
.orientation(gtk4::Orientation::Horizontal)
.spacing(15)
.margin_top(15)
.margin_bottom(15)
.margin_start(15)
.margin_end(15)
.css_classes(vec!["workspaces"])
.margin_top(10)
.margin_bottom(10)
.margin_start(10)
.margin_end(10)
.spacing(10)
.build();

let window = ApplicationWindow::builder()
.application(app)
.margin_bottom(15)
.margin_top(15)
.margin_start(15)
.margin_end(15)
.child(&workspaces_box)
.title("Hello, World!")
.build();

listen(
workspaces_box,
focus,
monitor,
data,
#[cfg(feature = "toast")]
Expand All @@ -75,12 +88,17 @@ fn activate(

window.init_layer_shell();
window.set_layer(Layer::Overlay);
window.set_opacity(0.95);
window.set_monitor(monitor);
window.present();

app.connect_activate(move |_| {
window.present();
});
}

fn listen(
workspaces_box: gtk4::Box,
focus: impl FnOnce(Client) + Copy + Send + 'static,
monitor: &Monitor,
data: Share,
#[cfg(feature = "toast")]
Expand All @@ -106,6 +124,7 @@ fn listen(
let first = data.lock().await;
update(
workspaces_box.clone(),
focus,
first,
&connector,
#[cfg(feature = "toast")]
Expand All @@ -117,6 +136,7 @@ fn listen(
let data = cvar.wait(data.lock().await).await;
update(
workspaces_box.clone(),
focus,
data,
&connector,
#[cfg(feature = "toast")]
Expand All @@ -128,6 +148,7 @@ fn listen(

fn update(
workspaces_box: gtk4::Box,
focus: impl FnOnce(Client) + Copy + Send + 'static,
data: MutexGuard<(Info, Data)>,
connector: &str,
#[cfg(feature = "toast")]
Expand Down Expand Up @@ -183,40 +204,65 @@ fn update(
.child(&fixed)
.build();

let mut active_ws = false;
for client in clients {
let frame = client_ui(client);
println!("ws: {workspace:?} > {} geo: x: {} y: {} width: {} height: {}",
client.class, ((client.at.0 - workspace.1.x as i16) / SIZE_FACTOR) as f64,
((client.at.1 - workspace.1.y as i16) / SIZE_FACTOR) as f64, (client.size.0 / SIZE_FACTOR) as i32,
(client.size.1 / SIZE_FACTOR) as i32);
fixed.put(&frame,
((client.at.0 - workspace.1.x as i16) / SIZE_FACTOR) as f64,
((client.at.1 - workspace.1.y as i16) / SIZE_FACTOR) as f64,
);
let active = data.1.active.as_ref().map_or(false, |active| active.address == client.address);
if active {
active_ws = true;
}
let frame = client_ui(client, active);
let x = ((client.at.0 - workspace.1.x as i16) / SIZE_FACTOR) as f64;
let y = ((client.at.1 - workspace.1.y as i16) / SIZE_FACTOR) as f64;
fixed.put(&frame, x, y);

let gesture = gtk4::GestureClick::new();
let client_clone = client.clone();
gesture.connect_pressed(move |gesture, _, _, _| {
gesture.set_state(gtk4::EventSequenceState::Claimed);
println!("clicked on {}", client_clone.class);
focus(client_clone.clone());
});
frame.add_controller(gesture);

let gesture_2 = gtk4::EventControllerMotion::new();
let client_clone_2 = client.clone();
gesture_2.connect_motion(move |_, _x, _y| {
println!("hovered on {}", client_clone_2.class);
focus(client_clone_2.clone());
});
// enable hover
// frame.add_controller(gesture_2);
}

if active_ws {
workspace_frame.add_css_class("active-ws");
}

workspaces_box.append(&workspace_frame);
}

// let clients = data.1.clients
// .iter()
// .filter(|client| {
// client.monitor == *monitor_data.0
// })
// .collect::<Vec<&Client>>();
}

pub fn start_gui(
focus: impl FnOnce(Client) + Copy + Send + 'static,
data: Share,
#[cfg(feature = "toast")]
do_toast: bool,
) {
let application = adw::Application::builder()
let application = Application::builder()
.application_id("com.github.h3rmt.window_switcher")
// .flags(ApplicationFlags::IS_LAUNCHER)
.build();

application.connect_activate(move |app| {
application.connect_startup(move |app| {
let provider = gtk4::CssProvider::new();
provider.load_from_string(CSS);

gtk4::style_context_add_provider_for_display(
&gdk::Display::default().expect("Could not connect to a display."),
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);

let monitors = get_all_monitors(
#[cfg(feature = "toast")]
do_toast
Expand All @@ -225,6 +271,7 @@ pub fn start_gui(
for monitor in monitors {
let data = data.clone();
activate(
focus,
app,
&monitor,
data,
Expand Down
18 changes: 11 additions & 7 deletions src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ use hyprland::data::{Client, Clients, Monitors, Workspace, Workspaces};
use hyprland::dispatch::{Dispatch, WindowIdentifier};
use hyprland::dispatch::DispatchType::FocusWindow;
use hyprland::prelude::*;
use hyprland::shared;
use hyprland::shared::WorkspaceId;

use crate::{Data, Info, MonitorData, MonitorId, WorkspaceData};
use crate::sort::{sort_clients, SortableClient, update_clients};

#[allow(clippy::too_many_arguments)]
pub fn handle(
pub fn find_next(
info: Info,
clients: Vec<Client>,
active: Option<Client>,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<Client, Box<dyn std::error::Error>> {
let active = active
.map(|a| (a.class, a.workspace.id, a.address.to_string()))
.map(|a| (a.class, a.workspace.id, a.address))
.unwrap_or_else(|| {
let a = clients.first().expect("No active client and no clients found");
(a.class.to_string(), a.workspace.id, a.address.to_string())
(a.class.clone(), a.workspace.id, a.address.clone())
});

let mut clients = clients;
Expand All @@ -42,7 +43,7 @@ pub fn handle(

let mut current_window_index = clients
.iter()
.position(|r| r.address.to_string() == active.2)
.position(|r| r.address == active.2)
.expect("Active window not found?");

if info.reverse {
Expand All @@ -65,13 +66,16 @@ pub fn handle(
println!("next_client: {:?}", next_client);
}

if !info.dry_run {
Ok(next_client)
}

pub fn execute(next_client: &Client, dry_run: bool) -> Result<(), shared::HyprError> {
if !dry_run {
Dispatch::call(FocusWindow(WindowIdentifier::Address(next_client.address.clone())))?;
} else {
// print regardless of verbose
println!("next_client: {}", next_client.title);
}

Ok(())
}

Expand Down
Loading

0 comments on commit 66a8a88

Please sign in to comment.