From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 6A9B39E4B4 for ; Tue, 31 Oct 2023 13:11:47 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 4A22B1B048 for ; Tue, 31 Oct 2023 13:11:17 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Tue, 31 Oct 2023 13:11:15 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 39F0242DF7 for ; Tue, 31 Oct 2023 13:11:15 +0100 (CET) From: Christoph Heiss To: pve-devel@lists.proxmox.com Date: Tue, 31 Oct 2023 13:10:54 +0100 Message-ID: <20231031121108.1130299-3-c.heiss@proxmox.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231031121108.1130299-1-c.heiss@proxmox.com> References: <20231031121108.1130299-1-c.heiss@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.015 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pve-devel] [PATCH installer v3 2/8] tui: views: add optional suffix label for `NumericEditView`s X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 31 Oct 2023 12:11:47 -0000 Most of the churn here is due to changing the inner view from an `EditView` to a `LinearLayout`. Also prompted the introduction of two small helpers .inner() and .inner_mut() to simplify things everywhere else in the view. Signed-off-by: Christoph Heiss --- Changes v1 -> v2: * no changes Changes v2 -> v3: * added #[allow(unused)] attribute to ::new_with_suffix(), to avoid warnings until it is actually used proxmox-tui-installer/src/views/mod.rs | 105 +++++++++++++++++++------ 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/proxmox-tui-installer/src/views/mod.rs b/proxmox-tui-installer/src/views/mod.rs index aa24fa4..8882ce9 100644 --- a/proxmox-tui-installer/src/views/mod.rs +++ b/proxmox-tui-installer/src/views/mod.rs @@ -19,16 +19,38 @@ mod timezone; pub use timezone::*; pub struct NumericEditView { - view: EditView, + view: LinearLayout, max_value: Option, max_content_width: Option, allow_empty: bool, } impl NumericEditView { + /// Creates a new [`NumericEditView`], with the value set to `0`. pub fn new() -> Self { + let view = LinearLayout::horizontal().child(EditView::new().content("0").full_width()); + + Self { + view, + max_value: None, + max_content_width: None, + allow_empty: false, + } + } + + /// Creates a new [`NumericEditView`], with the value set to `0` and a label to the right of it + /// with the given content, separated by a space. + /// + /// # Arguments + /// * `suffix` - Content for the label to the right of it. + #[allow(unused)] + pub fn new_with_suffix(suffix: &str) -> Self { + let view = LinearLayout::horizontal() + .child(EditView::new().content("0").full_width()) + .child(TextView::new(format!(" {suffix}"))); + Self { - view: EditView::new().content("0"), + view, max_value: None, max_content_width: None, allow_empty: false, @@ -42,7 +64,7 @@ impl NumericEditView { pub fn max_content_width(mut self, width: usize) -> Self { self.max_content_width = Some(width); - self.view.set_max_content_width(self.max_content_width); + self.inner_mut().set_max_content_width(Some(width)); self } @@ -50,24 +72,25 @@ impl NumericEditView { self.allow_empty = value; if value { - self.view = EditView::new(); + *self.inner_mut() = EditView::new(); } else { - self.view = EditView::new().content("0"); + *self.inner_mut() = EditView::new().content("0"); } - self.view.set_max_content_width(self.max_content_width); + let max_content_width = self.max_content_width; + self.inner_mut().set_max_content_width(max_content_width); self } pub fn get_content(&self) -> Result::Err> { assert!(!self.allow_empty); - self.view.get_content().parse() + self.inner().get_content().parse() } pub fn get_content_maybe(&self) -> Option::Err>> { - let content = self.view.get_content(); + let content = self.inner().get_content(); if !content.is_empty() { - Some(self.view.get_content().parse()) + Some(self.inner().get_content().parse()) } else { None } @@ -83,7 +106,7 @@ impl NumericEditView { if let Ok(val) = self.get_content() { if result.is_consumed() && val > max { // Restore the original value, before the insert - let cb = self.view.set_content((*original).clone()); + let cb = self.inner_mut().set_content((*original).clone()); return EventResult::with_cb_once(move |siv| { result.process(siv); cb(siv); @@ -94,16 +117,54 @@ impl NumericEditView { result } + + /// Provides an immutable reference to the inner [`EditView`]. + fn inner(&self) -> &EditView { + // Safety: Invariant; first child must always exist and be a `EditView` + self.view + .get_child(0) + .unwrap() + .downcast_ref::>() + .unwrap() + .get_inner() + } + + /// Provides a mutable reference to the inner [`EditView`]. + fn inner_mut(&mut self) -> &mut EditView { + // Safety: Invariant; first child must always exist and be a `EditView` + self.view + .get_child_mut(0) + .unwrap() + .downcast_mut::>() + .unwrap() + .get_inner_mut() + } + + /// Sets the content of the inner [`EditView`]. This correctly swaps out the content without + /// modifying the [`EditView`] in any way. + /// + /// Chainable variant. + /// + /// # Arguments + /// * `content` - New, stringified content for the inner [`EditView`]. Must be a valid value + /// according to the containet type `T`. + fn content_inner(mut self, content: &str) -> Self { + let mut inner = EditView::new(); + std::mem::swap(self.inner_mut(), &mut inner); + inner = inner.content(content); + std::mem::swap(self.inner_mut(), &mut inner); + self + } } pub type FloatEditView = NumericEditView; pub type IntegerEditView = NumericEditView; impl ViewWrapper for FloatEditView { - cursive::wrap_impl!(self.view: EditView); + cursive::wrap_impl!(self.view: LinearLayout); fn wrap_on_event(&mut self, event: Event) -> EventResult { - let original = self.view.get_content(); + let original = self.inner_mut().get_content(); let has_decimal_place = original.find('.').is_some(); @@ -114,13 +175,13 @@ impl ViewWrapper for FloatEditView { }; let decimal_places = self - .view + .inner_mut() .get_content() .split_once('.') .map(|(_, s)| s.len()) .unwrap_or_default(); if decimal_places > 2 { - let cb = self.view.set_content((*original).clone()); + let cb = self.inner_mut().set_content((*original).clone()); return EventResult::with_cb_once(move |siv| { result.process(siv); cb(siv); @@ -132,17 +193,17 @@ impl ViewWrapper for FloatEditView { } impl FloatEditView { - pub fn content(mut self, content: f64) -> Self { - self.view = self.view.content(format!("{:.2}", content)); - self + /// Sets the value of the [`FloatEditView`]. + pub fn content(self, content: f64) -> Self { + self.content_inner(&format!("{:.2}", content)) } } impl ViewWrapper for IntegerEditView { - cursive::wrap_impl!(self.view: EditView); + cursive::wrap_impl!(self.view: LinearLayout); fn wrap_on_event(&mut self, event: Event) -> EventResult { - let original = self.view.get_content(); + let original = self.inner_mut().get_content(); let result = match event { // Drop all other characters than numbers; allow dots if not set to integer-only @@ -155,9 +216,9 @@ impl ViewWrapper for IntegerEditView { } impl IntegerEditView { - pub fn content(mut self, content: usize) -> Self { - self.view = self.view.content(content.to_string()); - self + /// Sets the value of the [`IntegerEditView`]. + pub fn content(self, content: usize) -> Self { + self.content_inner(&content.to_string()) } } -- 2.42.0