all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH installer 4/6] fix #4829: tui: views: add optional suffix label for `NumericEditView`s
Date: Tue, 22 Aug 2023 12:24:52 +0200	[thread overview]
Message-ID: <20230822102533.295530-5-c.heiss@proxmox.com> (raw)
In-Reply-To: <20230822102533.295530-1-c.heiss@proxmox.com>

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 <c.heiss@proxmox.com>
---
This is also a precursory change to completely replace the
`DiskSizeEditView` component with just the `{Integer,Float}EditView` as
appropriate, the former essentially duplicating most of the latter
views.

 proxmox-tui-installer/src/views/mod.rs | 104 +++++++++++++++++++------
 1 file changed, 82 insertions(+), 22 deletions(-)

diff --git a/proxmox-tui-installer/src/views/mod.rs b/proxmox-tui-installer/src/views/mod.rs
index aa24fa4..e997968 100644
--- a/proxmox-tui-installer/src/views/mod.rs
+++ b/proxmox-tui-installer/src/views/mod.rs
@@ -19,16 +19,37 @@ mod timezone;
 pub use timezone::*;

 pub struct NumericEditView<T> {
-    view: EditView,
+    view: LinearLayout,
     max_value: Option<T>,
     max_content_width: Option<usize>,
     allow_empty: bool,
 }

 impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {
+    /// 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.
+    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 +63,7 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {

     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 +71,25 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {
         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<T, <T as FromStr>::Err> {
         assert!(!self.allow_empty);
-        self.view.get_content().parse()
+        self.inner().get_content().parse()
     }

     pub fn get_content_maybe(&self) -> Option<Result<T, <T as FromStr>::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 +105,7 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {
             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 +116,54 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {

         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::<ResizedView<EditView>>()
+            .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::<ResizedView<EditView>>()
+            .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<f64>;
 pub type IntegerEditView = NumericEditView<usize>;

 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 +174,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 +192,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 +215,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.41.0





  parent reply	other threads:[~2023-08-22 10:25 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-22 10:24 [pve-devel] [PATCH installer 0/6] fix #4829: set up a lower default limit for the ZFS ARC in the installer Christoph Heiss
2023-08-22 10:24 ` [pve-devel] [PATCH installer 1/6] fix #4829: install: add new ZFS `arc_max` setup option Christoph Heiss
2023-08-22 10:24 ` [pve-devel] [PATCH installer 2/6] fix #4829: proxinstall: expose new `arc_max` ZFS option for PVE installations Christoph Heiss
2023-08-22 10:24 ` [pve-devel] [PATCH installer 3/6] fix #4829: test: add tests for new zfs_arc_max calculations Christoph Heiss
2023-08-22 10:24 ` Christoph Heiss [this message]
2023-08-22 10:24 ` [pve-devel] [PATCH installer 5/6] fix #4829: tui: setup: add new ZFS `arc_max` option Christoph Heiss
2023-08-22 10:24 ` [pve-devel] [PATCH installer 6/6] fix #4829: tui: bootdisk: expose new `arc_max` ZFS option for PVE installations Christoph Heiss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230822102533.295530-5-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal