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 }