Skip to main content

oxihuman_viewer/
modifier_stack_view.rs

1#![allow(dead_code)]
2// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
3// SPDX-License-Identifier: Apache-2.0
4
5//! Modifier stack UI state.
6
7/// A single modifier slot in the stack.
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct ModifierSlot {
11    pub id: u32,
12    pub name: String,
13    pub enabled: bool,
14    pub expanded: bool,
15    pub order: u32,
16}
17
18/// The modifier stack panel state.
19#[allow(dead_code)]
20#[derive(Debug, Clone, Default)]
21pub struct ModifierStackView {
22    pub modifiers: Vec<ModifierSlot>,
23}
24
25/// Create a new empty `ModifierStackView`.
26#[allow(dead_code)]
27pub fn new_modifier_stack_view() -> ModifierStackView {
28    ModifierStackView::default()
29}
30
31/// Add a modifier slot.  Order defaults to the current length.
32#[allow(dead_code)]
33pub fn add_modifier_slot(view: &mut ModifierStackView, id: u32, name: &str) {
34    let order = view.modifiers.len() as u32;
35    view.modifiers.push(ModifierSlot {
36        id,
37        name: name.to_string(),
38        enabled: true,
39        expanded: false,
40        order,
41    });
42}
43
44/// Toggle the `enabled` flag of the modifier with the given id.
45#[allow(dead_code)]
46pub fn toggle_modifier(view: &mut ModifierStackView, id: u32) {
47    if let Some(slot) = view.modifiers.iter_mut().find(|m| m.id == id) {
48        slot.enabled = !slot.enabled;
49    }
50}
51
52/// Move the modifier with `id` one step up (lower order index) in the stack.
53#[allow(dead_code)]
54pub fn move_modifier_up(view: &mut ModifierStackView, id: u32) {
55    if let Some(pos) = view.modifiers.iter().position(|m| m.id == id) {
56        if pos > 0 {
57            view.modifiers.swap(pos, pos - 1);
58            // update order fields
59            for (i, m) in view.modifiers.iter_mut().enumerate() {
60                m.order = i as u32;
61            }
62        }
63    }
64}
65
66/// Return the number of modifier slots.
67#[allow(dead_code)]
68pub fn modifier_count(view: &ModifierStackView) -> usize {
69    view.modifiers.len()
70}
71
72// ── Tests ─────────────────────────────────────────────────────────────────────
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn new_view_is_empty() {
79        let v = new_modifier_stack_view();
80        assert_eq!(modifier_count(&v), 0);
81    }
82
83    #[test]
84    fn add_modifier_increments_count() {
85        let mut v = new_modifier_stack_view();
86        add_modifier_slot(&mut v, 0, "Subsurf");
87        assert_eq!(modifier_count(&v), 1);
88    }
89
90    #[test]
91    fn added_modifier_is_enabled() {
92        let mut v = new_modifier_stack_view();
93        add_modifier_slot(&mut v, 0, "Armature");
94        assert!(v.modifiers[0].enabled);
95    }
96
97    #[test]
98    fn toggle_modifier_flips_enabled() {
99        let mut v = new_modifier_stack_view();
100        add_modifier_slot(&mut v, 1, "Mirror");
101        toggle_modifier(&mut v, 1);
102        assert!(!v.modifiers[0].enabled);
103    }
104
105    #[test]
106    fn toggle_modifier_unknown_id_no_panic() {
107        let mut v = new_modifier_stack_view();
108        toggle_modifier(&mut v, 99); // must not panic
109    }
110
111    #[test]
112    fn move_modifier_up_changes_order() {
113        let mut v = new_modifier_stack_view();
114        add_modifier_slot(&mut v, 10, "A");
115        add_modifier_slot(&mut v, 11, "B");
116        move_modifier_up(&mut v, 11);
117        assert_eq!(v.modifiers[0].id, 11);
118    }
119
120    #[test]
121    fn move_modifier_up_top_no_change() {
122        let mut v = new_modifier_stack_view();
123        add_modifier_slot(&mut v, 10, "A");
124        move_modifier_up(&mut v, 10); // already at top, must not panic
125        assert_eq!(v.modifiers[0].id, 10);
126    }
127
128    #[test]
129    fn move_modifier_up_unknown_no_panic() {
130        let mut v = new_modifier_stack_view();
131        move_modifier_up(&mut v, 99); // must not panic
132    }
133
134    #[test]
135    fn modifier_count_multiple() {
136        let mut v = new_modifier_stack_view();
137        for i in 0..5u32 {
138            add_modifier_slot(&mut v, i, "mod");
139        }
140        assert_eq!(modifier_count(&v), 5);
141    }
142
143    #[test]
144    fn order_assigned_sequentially() {
145        let mut v = new_modifier_stack_view();
146        add_modifier_slot(&mut v, 0, "A");
147        add_modifier_slot(&mut v, 1, "B");
148        assert_eq!(v.modifiers[0].order, 0);
149        assert_eq!(v.modifiers[1].order, 1);
150    }
151}