1 module rpui.widgets.text_input.number_input_type_component;
2 
3 import std.conv;
4 import std.math;
5 
6 import rpui.math;
7 import rpui.primitives;
8 import rpui.events;
9 import rpui.widgets.text_input.widget;
10 import rpui.render.components;
11 
12 struct NumberInputTypeComponent {
13     struct Arrow {
14         vec2 absolutePosition;
15         Rect area;
16         bool isEnter = false;
17 
18         @property inout(State) state() inout {
19             return isEnter ? State.enter : State.leave;
20         }
21     }
22 
23     Arrow leftArrow;
24     Arrow rightArrow;
25 
26     TextInput textInput;
27     float delta = 0;
28     bool isClick = false;
29     int startX = 0;
30     int startY = 0;
31     float value = 0;
32     float initialValue = 0;
33     float mouseSensetive = 20.0f;
34     bool avoidFocusing = false;
35 
36     void attach(TextInput textInput) {
37         this.textInput = textInput;
38     }
39 
40     void onMouseDown(in MouseDownEvent event) {
41         if (leftArrow.isEnter) {
42             textInput.blur();
43             avoidFocusing = true;
44             addValue(-1);
45             return;
46         }
47 
48         if (rightArrow.isEnter) {
49             textInput.blur();
50             avoidFocusing = true;
51             addValue(1);
52             return;
53         }
54 
55         if (!textInput.isEnter || textInput.isFocused)
56             return;
57 
58         isClick = true;
59         delta = 0;
60         startX = event.x;
61         startY = event.y;
62         initialValue = value;
63         avoidFocusing = false;
64         textInput.view.hideCursor();
65         textInput.freezeUI();
66     }
67 
68     void onMouseMove(in MouseMoveEvent event) {
69         if (!isClick)
70             return;
71 
72         delta = (event.x - startX) / mouseSensetive;
73 
74         if (delta > 0) {
75             addValue(floor(delta).to!int);
76         } else {
77             addValue(ceil(delta).to!int);
78         }
79 
80         if (abs(delta) >= 1) {
81             textInput.view.setMousePositon(startX, startY);
82             avoidFocusing = true;
83             delta = 0;
84         }
85     }
86 
87     void onMouseUp(in MouseUpEvent event) {
88         textInput.unfreezeUI();
89 
90         if (avoidFocusing) {
91             avoidFocusing = false;
92             textInput.blur();
93             // TODO: notify on change event
94         }
95 
96         delta = 0;
97         startX = 0;
98         isClick = false;
99         textInput.view.showCursor();
100     }
101 
102     void onBlur() {
103     }
104 
105     void addValue(in float sign) {
106         if (sign == 0)
107             return;
108 
109         value = textInput.text.to!(float) + sign * textInput.numberStep;
110         textInput.text = value.to!(dstring);
111     }
112 
113     void updateArrowStates() {
114         const areaSize = vec2(textInput.measure.arrowsAreaWidth, textInput.size.y);
115 
116         leftArrow.area = Rect(
117             textInput.absolutePosition,
118             areaSize
119         );
120 
121         rightArrow.area = Rect(
122             textInput.absolutePosition + vec2(textInput.size.x - areaSize.x, 0),
123             areaSize
124         );
125 
126         if (isClick) {
127             leftArrow.isEnter = false;
128             rightArrow.isEnter = false;
129         } else {
130             leftArrow.isEnter = pointInRect(textInput.view.mousePos, leftArrow.area);
131             rightArrow.isEnter = pointInRect(textInput.view.mousePos, rightArrow.area);
132         }
133     }
134 }