termux_gui/components/
spinner.rs

1//! Spinner (dropdown) component
2//!
3//! ## Important Notes on Event Handling
4//!
5//! When handling `itemselected` events from a Spinner:
6//! - The event's `"selected"` field contains the **selected text as a string**, not an index
7//! - You should match against the string value, not the index position
8//! - Example event structure: `{"aid": 0, "id": 123, "selected": "Option 2"}`
9//!
10//! ## Example
11//!
12//! ```rust,no_run
13//! use termux_gui::{Activity, Result};
14//! use termux_gui::connection::read_message;
15//!
16//! # fn main() -> Result<()> {
17//! let mut activity = Activity::new(false)?;
18//! let layout = activity.create_linear_layout(None)?;
19//!
20//! let spinner = activity.create_spinner(Some(layout.id()))?;
21//! spinner.set_list(&mut activity, &["Option 1", "Option 2", "Option 3"])?;
22//!
23//! // Event handling - use string matching, not index!
24//! loop {
25//!     let event = read_message(activity.event_stream())?;
26//!     if event["type"] == "itemselected" {
27//!         let selected_text = event["value"]["selected"].as_str().unwrap_or("");
28//!         match selected_text {
29//!             "Option 1" => println!("First option selected"),
30//!             "Option 2" => println!("Second option selected"),
31//!             _ => {}
32//!         }
33//!     }
34//! }
35//! # }
36//! ```
37
38use serde_json::json;
39use crate::activity::Activity;
40use crate::view::View;
41use crate::error::Result;
42
43/// A Spinner is a dropdown list
44///
45/// **Important**: When handling `itemselected` events, the `"selected"` field 
46/// contains the selected text as a string, not an index number.
47pub struct Spinner {
48    view: View,
49    aid: i64,
50}
51
52impl Spinner {
53    /// Create a new Spinner
54    pub fn new(activity: &mut Activity, parent: Option<i64>) -> Result<Self> {
55        let mut params = json!({
56            "aid": activity.id()
57        });
58        
59        // Only set parent if explicitly provided
60        if let Some(parent_id) = parent {
61            params["parent"] = json!(parent_id);
62        }
63        
64        let response = activity.send_read(&json!({
65            "method": "createSpinner",
66            "params": params
67        }))?;
68        
69        let id = response
70            .as_i64()
71            .ok_or_else(|| crate::error::GuiError::InvalidResponse("Invalid id".to_string()))?;
72        
73        Ok(Spinner {
74            view: View::new(id),
75            aid: activity.id(),
76        })
77    }
78    
79    /// Get the view ID
80    pub fn id(&self) -> i64 {
81        self.view.id()
82    }
83    
84    /// Get the underlying View
85    pub fn view(&self) -> &View {
86        &self.view
87    }
88    
89    /// Set the list of options
90    pub fn set_list(&self, activity: &mut Activity, items: &[&str]) -> Result<()> {
91        activity.send(&json!({
92            "method": "setList",
93            "params": {
94                "aid": self.aid,
95                "id": self.view.id(),
96                "list": items
97            }
98        }))?;
99        Ok(())
100    }
101    
102    /// Select an item by index
103    pub fn select_item(&self, activity: &mut Activity, index: i32) -> Result<()> {
104        activity.send(&json!({
105            "method": "selectItem",
106            "params": {
107                "aid": self.aid,
108                "id": self.view.id(),
109                "item": index
110            }
111        }))?;
112        Ok(())
113    }
114    
115    /// Refresh the spinner (needed after setList to ensure display is updated)
116    pub fn refresh(&self, activity: &mut Activity) -> Result<()> {
117        activity.send(&json!({
118            "method": "refreshSpinner",
119            "params": {
120                "aid": self.aid,
121                "id": self.view.id()
122            }
123        }))?;
124        Ok(())
125    }
126}