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 }