1 /**
2 Widget for displaying an editable textbox. 
3 
4 User must click on widget to edit, and mouse must be over box while editing.
5 
6 Copyright: Ethan Reker 2017.
7 Copyright: Copyright Auburn Sounds 2015-2017.
8 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 */
10 module dplug.pbrwidgets.textbox;
11 
12 import dplug.gui.element;
13 import dplug.core.nogc;
14 import dplug.core.vec;
15 import dplug.window.window : getCharFromKey;
16 
17 private import core.stdc.stdlib : malloc, free;
18 private import core.stdc.stdio : snprintf, printf;
19 private import core.stdc.string : strcmp, strlen;
20 
21 class UITextBox : UIElement
22 {
23 public:
24 nothrow:
25 @nogc:
26 
27     this(UIContext context, Font font, int textSize, RGBA textColor = RGBA(200, 200, 200, 255), RGBA backgroundColor = RGBA(0, 0, 0, 255))
28     {
29         super(context, flagPBR);
30         _font = font;
31         _textSize = textSize;
32         _textColor = textColor;
33         _backgroundColor = backgroundColor;
34         charBuffer = makeVec!char();
35     }
36 
37     ~this()
38     {
39     }
40 
41     @property const(char)[] getText()
42     {
43         return displayString();
44     }
45 
46     override void onDrawPBR(ImageRef!RGBA diffuseMap, ImageRef!L16 depthMap, ImageRef!RGBA materialMap, box2i[] dirtyRects)
47     {
48         float textPosx = position.width * 0.5f;
49         float textPosy = position.height * 0.5f;
50 
51         foreach(dirtyRect; dirtyRects)
52         {
53             auto croppedDiffuse = diffuseMap.cropImageRef(dirtyRect);
54             vec2f positionInDirty = vec2f(textPosx, textPosy) - dirtyRect.min;
55 
56             croppedDiffuse.fillAll(_backgroundColor);
57             croppedDiffuse.fillText(_font, displayString(), _textSize, 0.5, _textColor, positionInDirty.x, positionInDirty.y);
58         }
59     }
60 
61     override Click onMouseClick(int x, int y, int button, bool isDoubleClick, MouseState mstate)
62     {
63         // Left click
64         _isActive = true;
65 
66         setDirtyWhole();
67         return Click.startDrag;
68     }
69 
70     override void onMouseEnter()
71     {
72         setDirtyWhole();
73     }
74 
75     override void onMouseExit()
76     {
77         _isActive = false;
78         setDirtyWhole();
79     }
80 
81     override bool onKeyDown(Key key)
82     {
83         if(_isActive)
84         {
85             const char c = cast(char)getCharFromKey(key);
86             if(c == '\t')
87             {
88                 if (charBuffer.length > 0)
89                 {
90                     charBuffer.popBack();
91                 }
92             }
93             else if(c != '\0')
94                 charBuffer.pushBack(c);
95             setDirtyWhole();
96             return true;
97         }
98 
99         return false;
100     }
101 
102 private:
103 
104     Font _font;
105     int _textSize;
106     RGBA _textColor;
107     RGBA _backgroundColor;
108     bool _isActive;
109     Vec!char charBuffer;
110 
111     const(char)[] displayString() nothrow @nogc
112     {
113         return charBuffer[];
114     }
115 
116     final bool containsPoint(int x, int y)
117     {
118         box2i subSquare = getSubsquare();
119         float centerx = (subSquare.min.x + subSquare.max.x - 1) * 0.5f;
120         float centery = (subSquare.min.y + subSquare.max.y - 1) * 0.5f;
121 
122         float minx = centerx - (_position.width / 2);
123         float maxx = centerx + (_position.width / 2);
124         float miny = centery - (_position.height / 2);
125         float maxy = centery + (_position.height / 2);
126 
127         return x > minx && x < maxx && y > miny && y < maxy;
128     }
129 
130     /// Returns: largest square centered in _position
131     final box2i getSubsquare() pure const
132     {
133         // We'll draw entirely in the largest centered square in _position.
134         box2i subSquare;
135         if (_position.width > _position.height)
136         {
137             int offset = (_position.width - _position.height) / 2;
138             int minX = offset;
139             subSquare = box2i(minX, 0, minX + _position.height, _position.height);
140         }
141         else
142         {
143             int offset = (_position.height - _position.width) / 2;
144             int minY = offset;
145             subSquare = box2i(0, minY, _position.width, minY + _position.width);
146         }
147         return subSquare;
148     }
149 
150     final float getRadius() pure const
151     {
152         return getSubsquare().width * 0.5f;
153 
154     }
155 
156     final vec2f getCenter() pure const
157     {
158         box2i subSquare = getSubsquare();
159         float centerx = (subSquare.min.x + subSquare.max.x - 1) * 0.5f;
160         float centery = (subSquare.min.y + subSquare.max.y - 1) * 0.5f;
161         return vec2f(centerx, centery);
162     }
163 
164 }