From 0b1799a41039b17c9ac8aa91467d15ca72a3ecfd Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 6 Dec 2024 22:34:26 +0200 Subject: [PATCH] Add support for window resizing --- src/frontend.rs | 22 ++++++++++++- src/frontend/running.rs | 4 +++ src/frontend/running/farm.rs | 63 ++++++++++++++++++++++++++++-------- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/src/frontend.rs b/src/frontend.rs index 023fa73..933384a 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -28,6 +28,7 @@ use std::cell::{Cell, LazyCell}; use std::future::Future; use std::path::PathBuf; use std::rc::Rc; +use std::sync::atomic::{AtomicI32, Ordering}; use std::{env, fmt}; use tracing::{debug, error, warn}; @@ -116,6 +117,7 @@ pub enum AppInput { StartUpgrade, Restart, CloseStatusBarWarning, + WindowResized, HideWindow, ShowWindow, ShowHideToggle, @@ -281,7 +283,6 @@ impl AsyncComponent for App { view! { gtk::Window { set_decorated: false, - set_resizable: false, set_size_request: (800, 600), #[track = "model.changed_current_view()"] set_title: Some(&format!("{} - Space Acres {}", model.current_view.title(), env!("CARGO_PKG_VERSION"))), @@ -721,6 +722,22 @@ impl AsyncComponent for App { }); } + // Hacky way to track window size changes + if let Some(surface) = root.surface() { + surface.connect_notify(Some("width"), { + let sender = sender.clone(); + let last_width = AtomicI32::new(0); + + move |surface, _new_state_param| { + let new_surface_width = surface.width(); + let old_surface_width = last_width.swap(new_surface_width, Ordering::AcqRel); + if new_surface_width != old_surface_width { + sender.input(AppInput::WindowResized); + } + } + }); + } + AsyncComponentParts { model, widgets } } @@ -794,6 +811,9 @@ impl AsyncComponent for App { AppInput::CloseStatusBarWarning => { self.set_status_bar_contents(StatusBarContents::None); } + AppInput::WindowResized => { + self.running_view.emit(RunningInput::WindowResized); + } AppInput::HideWindow => { root.set_visible(false); } diff --git a/src/frontend/running.rs b/src/frontend/running.rs index dbcdb6f..ed168fc 100644 --- a/src/frontend/running.rs +++ b/src/frontend/running.rs @@ -51,6 +51,7 @@ pub enum RunningInput { FarmerNotification(FarmerNotification), ToggleFarmDetails, TogglePausePlotting, + WindowResized, } #[derive(Debug)] @@ -493,6 +494,9 @@ impl RunningView { debug!("Failed to send RunningOutput::TogglePausePlotting"); } } + RunningInput::WindowResized => { + self.farms.broadcast(FarmWidgetInput::WindowResized); + } } } diff --git a/src/frontend/running/farm.rs b/src/frontend/running/farm.rs index 46f8f25..dda46df 100644 --- a/src/frontend/running/farm.rs +++ b/src/frontend/running/farm.rs @@ -6,6 +6,7 @@ use bytesize::ByteSize; use gtk::prelude::*; use notify_rust::Notification; use relm4::prelude::*; +use relm4::{RelmIterChildrenExt, RelmRemoveAllExt}; use simple_moving_average::{SingleSumSMA, SMA}; use std::collections::HashMap; use std::path::PathBuf; @@ -19,8 +20,6 @@ use subspace_farmer::farm::{ use tracing::{error, warn}; const INVALID_SCORE_VALUE: f64 = -1.0; -/// Experimentally found number that is good for default window size to not have horizontal scroll -const SECTORS_PER_ROW: usize = 108; /// Number of samples over which to track auditing time, 1 minute in slots const AUDITING_TIME_TRACKING_WINDOW: usize = 60; /// 500ms auditing time is excellent, anything larger will result in auditing performance indicator decrease @@ -99,6 +98,8 @@ pub(super) enum FarmWidgetInput { Error { error: Arc, }, + WindowResized, + RecalculateSectorRows, } #[tracker::track] @@ -380,11 +381,14 @@ impl FactoryComponent for FarmWidget { }, }, - gtk::Box { - #[track = "self.changed_farm_details() || self.changed_error()"] - set_visible: self.farm_details && self.error.is_none(), + // A hack to allow measuring width for sectors + gtk::ScrolledWindow { + set_vscrollbar_policy: gtk::PolicyType::Never, - self.sector_rows.clone(), + self.sector_rows.clone() -> gtk::Box { + #[track = "self.changed_farm_details() || self.changed_error()"] + set_visible: self.farm_details && self.error.is_none(), + }, }, }, } @@ -394,6 +398,7 @@ impl FactoryComponent for FarmWidget { for sector_index in 0..init.total_sectors { let sector = gtk::Box::builder() .css_name("farm-sector") + .halign(gtk::Align::Start) .tooltip_text(T.running_farmer_farm_sector(sector_index).as_str()) .build(); if sector_index < init.plotted_total_sectors { @@ -404,13 +409,6 @@ impl FactoryComponent for FarmWidget { } let sector_rows = gtk::Box::new(gtk::Orientation::Vertical, 0); - sectors.chunks(SECTORS_PER_ROW).for_each(|sectors| { - let sector_row = gtk::Box::new(gtk::Orientation::Horizontal, 0); - sector_rows.append(§or_row); - for sector in sectors { - sector_row.append(sector); - } - }); Self { path: init.farm.directory, @@ -585,6 +583,7 @@ impl FarmWidget { } FarmWidgetInput::ToggleFarmDetails => { self.set_farm_details(!self.farm_details); + sender.input(FarmWidgetInput::RecalculateSectorRows); } FarmWidgetInput::Error { error } => { sender.spawn_command(|_sender| { @@ -602,6 +601,44 @@ impl FarmWidget { self.get_mut_error().replace(error); } + FarmWidgetInput::WindowResized => { + sender.input(FarmWidgetInput::RecalculateSectorRows); + } + FarmWidgetInput::RecalculateSectorRows => { + if !self.farm_details { + return; + } + + let sector_rows_width = self + .sector_rows + .parent() + .expect("Always attached to parent; qed") + .width(); + // TODO: This is a hardcoded number, but it is tricky to determine its size in + // runtime, so will do for now + let sector_width = 7; + let sectors_per_row = sector_rows_width / sector_width; + + if sectors_per_row as usize == self.sector_rows.iter_children().count() { + // Nothing has changed + return; + } + + let mut sectors_iter = self.sectors.values(); + + self.sector_rows.remove_all(); + for _ in 0..self.sectors.len().div_ceil(sectors_per_row as usize) { + let sector_row = gtk::Box::new(gtk::Orientation::Horizontal, 0); + + for _ in 0..sectors_per_row { + if let Some(sector) = sectors_iter.next() { + sector_row.append(sector); + } + } + + self.sector_rows.append(§or_row); + } + } } }