[pve-devel] [PATCH installer v3 1/3] tui: NumericEditView: add optional placeholder value

Christoph Heiss c.heiss at proxmox.com
Mon Apr 15 15:20:25 CEST 2024


Enables to add an optional placeholder value to `NumericEditView`, which
will be displayed in a different (darker) color and not returned by
`.get_content*()`.

Can be used for having default values in the TUI, but with different
handling in the back.

Signed-off-by: Christoph Heiss <c.heiss at proxmox.com>
---
Changes v2 -> v3:
  * when empty & focused, do not show the placeholder value at all

Changes v1 -> v2:
  * new patch

 proxmox-tui-installer/src/views/mod.rs | 66 ++++++++++++++++++++++++--
 1 file changed, 62 insertions(+), 4 deletions(-)

diff --git a/proxmox-tui-installer/src/views/mod.rs b/proxmox-tui-installer/src/views/mod.rs
index 3244e76..5e5f4fb 100644
--- a/proxmox-tui-installer/src/views/mod.rs
+++ b/proxmox-tui-installer/src/views/mod.rs
@@ -2,9 +2,10 @@ use std::{net::IpAddr, rc::Rc, str::FromStr};

 use cursive::{
     event::{Event, EventResult},
+    theme::BaseColor,
     view::{Resizable, ViewWrapper},
     views::{EditView, LinearLayout, NamedView, ResizedView, SelectView, TextView},
-    Rect, Vec2, View,
+    Printer, Rect, Vec2, View,
 };

 use proxmox_installer_common::utils::CidrAddress;
@@ -24,6 +25,7 @@ pub use timezone::*;
 pub struct NumericEditView<T> {
     view: LinearLayout,
     max_value: Option<T>,
+    placeholder: Option<T>,
     max_content_width: Option<usize>,
     allow_empty: bool,
 }
@@ -36,6 +38,7 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {
         Self {
             view,
             max_value: None,
+            placeholder: None,
             max_content_width: None,
             allow_empty: false,
         }
@@ -54,6 +57,7 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {
         Self {
             view,
             max_value: None,
+            placeholder: None,
             max_content_width: None,
             allow_empty: false,
         }
@@ -84,15 +88,42 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {
         self
     }

+    /// Sets a placeholder value for this view. Implies `allow_empty(true)`.
+    /// Implies `allow_empty(true)`.
+    ///
+    /// # Arguments
+    /// `placeholder` - The placeholder value to set for this view.
+    #[allow(unused)]
+    pub fn placeholder(mut self, placeholder: T) -> Self {
+        self.placeholder = Some(placeholder);
+        self.allow_empty(true)
+    }
+
+    /// Returns the current value of the view. If a placeholder is defined and
+    /// no value is currently set, the placeholder value is returned.
+    ///
+    /// **This should only be called when `allow_empty = false` or a placeholder
+    /// is set.**
     pub fn get_content(&self) -> Result<T, <T as FromStr>::Err> {
-        assert!(!self.allow_empty);
-        self.inner().get_content().parse()
+        let content = self.inner().get_content();
+
+        if content.is_empty() {
+            if let Some(placeholder) = self.placeholder {
+                return Ok(placeholder);
+            }
+        }
+
+        assert!(!(self.allow_empty && self.placeholder.is_none()));
+        content.parse()
     }

+    /// Returns the current value of the view, or [`None`] if the view is
+    /// currently empty.
     pub fn get_content_maybe(&self) -> Option<Result<T, <T as FromStr>::Err>> {
         let content = self.inner().get_content();
+
         if !content.is_empty() {
-            Some(self.inner().get_content().parse())
+            Some(content.parse())
         } else {
             None
         }
@@ -157,6 +188,25 @@ impl<T: Copy + ToString + FromStr + PartialOrd> NumericEditView<T> {
         std::mem::swap(self.inner_mut(), &mut inner);
         self
     }
+
+    /// Generic `wrap_draw()` implementation for [`ViewWrapper`].
+    ///
+    /// # Arguments
+    /// * `printer` - The [`Printer`] to draw to the base view.
+    fn wrap_draw_inner(&self, printer: &Printer) {
+        self.view.draw(printer);
+
+        if self.inner().get_content().is_empty() && !printer.focused {
+            if let Some(placeholder) = self.placeholder {
+                let placeholder = placeholder.to_string();
+
+                printer.with_color(
+                    (BaseColor::Blue.light(), BaseColor::Blue.dark()).into(),
+                    |printer| printer.print((0, 0), &placeholder),
+                );
+            }
+        }
+    }
 }

 pub type FloatEditView = NumericEditView<f64>;
@@ -165,6 +215,10 @@ pub type IntegerEditView = NumericEditView<usize>;
 impl ViewWrapper for FloatEditView {
     cursive::wrap_impl!(self.view: LinearLayout);

+    fn wrap_draw(&self, printer: &Printer) {
+        self.wrap_draw_inner(printer);
+    }
+
     fn wrap_on_event(&mut self, event: Event) -> EventResult {
         let original = self.inner_mut().get_content();

@@ -204,6 +258,10 @@ impl FloatEditView {
 impl ViewWrapper for IntegerEditView {
     cursive::wrap_impl!(self.view: LinearLayout);

+    fn wrap_draw(&self, printer: &Printer) {
+        self.wrap_draw_inner(printer);
+    }
+
     fn wrap_on_event(&mut self, event: Event) -> EventResult {
         let original = self.inner_mut().get_content();

--
2.44.0





More information about the pve-devel mailing list