use crate::shared::workspace_clients::{
WorkspaceClients, WorkspaceClientsInit, WorkspaceClientsInput, WorkspaceClientsOutput,
};
use core_lib::{ClientData, ClientId, MonitorData, WorkspaceData, WorkspaceId};
use regex::Regex;
use relm4::adw::gtk;
use relm4::adw::prelude::*;
use relm4::prelude::*;
#[derive(Debug)]
pub struct Workspaces {
active: bool,
pub data: WorkspaceData,
pub workspace_id: WorkspaceId,
pub remove_html: Regex,
pub scale: f64,
pub monitor_data: MonitorData,
pub clients: FactoryVecDeque<WorkspaceClients>,
}
#[derive(Debug)]
pub enum WorkspacesInput {
SetActive(bool),
SetActiveClient(ClientId),
}
#[derive(Debug)]
pub struct WorkspacesInit {
pub monitor_data: MonitorData,
pub data: WorkspaceData,
pub id: WorkspaceId,
pub remove_html: Regex,
pub scale: f64,
pub clients: Vec<(ClientId, ClientData)>,
}
#[derive(Debug)]
pub enum WorkspacesOutput {
Clicked(WorkspaceId),
ClickedC(ClientId),
}
#[relm4::factory(pub)]
impl FactoryComponent for Workspaces {
type Init = WorkspacesInit;
type Input = WorkspacesInput;
type Output = WorkspacesOutput;
type CommandOutput = ();
type ParentWidget = gtk::FlowBox;
view! {
gtk::FlowBoxChild {
gtk::Box {
#[watch]
set_css_classes: &workspace_css_classes(self.active, self.workspace_id),
set_width_request: scale(self.monitor_data.width, self.scale),
set_height_request: scale(self.monitor_data.height, self.scale),
gtk::Frame {
#[wrap(Some)]
set_label_widget = >k::Button {
set_cursor_from_name: Some("pointer"),
set_width_request: scale(self.monitor_data.width, self.scale),
connect_clicked[sender, id = self.workspace_id] => move |_| sender.output_sender().emit(WorkspacesOutput::Clicked(id)),
gtk::Label {
set_label: &self.workspace_label(),
}
},
set_label_align: 0.5,
self.clients.widget() -> >k::Fixed {
set_width_request: scale(self.data.width, self.scale),
set_height_request: scale(self.data.height, self.scale),
}
}
}
}
}
fn init_model(init: Self::Init, _index: &DynamicIndex, sender: FactorySender<Self>) -> Self {
let mut clients: FactoryVecDeque<WorkspaceClients> = FactoryVecDeque::builder()
.launch(gtk::Fixed::default())
.forward(sender.output_sender(), |msg| match msg {
WorkspaceClientsOutput::Clicked(id) => WorkspacesOutput::ClickedC(id),
});
{
let mut sorted_clients: Vec<_> = init.clients.iter().collect();
sorted_clients.sort_by(|(_, a), (_, b)| {
if a.floating && b.floating {
(b.width as i32 * b.height as i32).cmp(&(a.width as i32 * a.height as i32))
} else {
a.floating.cmp(&b.floating)
}
});
let mut guard = clients.guard();
for (id, client) in sorted_clients {
if client.enabled {
guard.push_back(WorkspaceClientsInit {
id: *id,
scale: init.scale,
data: client.clone(),
});
}
}
}
Self {
active: false,
data: init.data,
monitor_data: init.monitor_data,
workspace_id: init.id,
remove_html: init.remove_html,
scale: init.scale,
clients,
}
}
fn update(&mut self, msg: Self::Input, _sender: FactorySender<Self>) {
match msg {
WorkspacesInput::SetActive(active) => {
self.active = active;
for (idx, _) in self.clients.iter().enumerate() {
self.clients
.send(idx, WorkspaceClientsInput::SetActive(false));
}
}
WorkspacesInput::SetActiveClient(id) => {
self.active = false;
for (idx, item) in self.clients.iter().enumerate() {
self.clients
.send(idx, WorkspaceClientsInput::SetActive(id == item.id));
}
}
};
}
}
impl Workspaces {
fn workspace_label(&self) -> String {
if self.data.name.trim().is_empty() {
self.workspace_id.to_string()
} else {
self.remove_html
.replace_all(&self.data.name, "")
.to_string()
}
}
pub fn get_client_id(&self, idx: usize) -> Option<ClientId> {
self.clients.get(idx).map(|c| c.id)
}
pub fn client_count(&self) -> usize {
self.clients.len()
}
}
fn scale<T: Into<f64>>(value: T, scale: f64) -> i32 {
(value.into() / (15f64 - scale)) as i32
}
fn workspace_css_classes(active: bool, id: WorkspaceId) -> Vec<&'static str> {
let mut classes = vec!["workspace", "no-hover"];
if active {
classes.push("active");
}
if id < 0 {
classes.push("special");
}
classes
}