1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.widgets.inputtext;
8 import creator.widgets;
9 import creator.core;
10 import inochi2d;
11 import bindbc.sdl;
12 import std.stdio;
13 import std.string;
14 import core.memory : GC;
15 
16 private {
17 
18     struct TextCallbackUserData {
19         string* str;
20     }
21 }
22 
23 /**
24     D compatible text input
25 */
26 bool incInputText(string wId, ref string buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags.None) {
27     auto available = incAvailableSpace();
28     return incInputText(wId, available.x, buffer, flags);
29 }
30 
31 /**
32     D compatible text input
33 */
34 bool incInputText(string wId, float width, ref string buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags.None) {
35 
36     // NOTE: null strings would result in segfault, make sure it's at least just empty.
37     if (buffer.ptr is null) {
38         buffer = "";
39     }
40 
41     if (buffer.ptr[buffer.length] != '\0') {
42         // If buffer.ptr does not end with '\0', recreate string to force '\0' at the end.
43         buffer = buffer.ptr[0..buffer.length]~'\0';
44     }
45 
46     // Push ID
47     auto id = igGetID(wId.ptr, wId.ptr+wId.length);
48     igPushID(id);
49     scope(exit) igPopID();
50 
51     // Set desired width
52     igPushItemWidth(width);
53     scope(success) igPopItemWidth();
54 
55     // Create callback data
56     TextCallbackUserData cb;
57     cb.str = &buffer;
58 
59     // Call ImGui's input handling
60     if (igInputText(
61         "###INPUT",
62         cast(char*)buffer.ptr, 
63         buffer.length+1,
64         flags | ImGuiInputTextFlags.CallbackResize,
65         cast(ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data) {
66             TextCallbackUserData* udata = cast(TextCallbackUserData*)data.UserData;
67 
68             // Allow resizing strings on GC heap
69             if (data.EventFlag == ImGuiInputTextFlags.CallbackResize) {
70 
71                 // Make sure the buffer doesn't become negatively sized.
72                 if (data.BufTextLen < 0) data.BufTextLen = 0;
73 
74                 // Resize and pass buffer ptr in
75                 (*udata.str).length = data.BufTextLen+1;
76 
77                 // slice out the null terminator
78                 data.Buf = cast(char*)(*udata.str).ptr;
79                 data.Buf[data.BufTextLen] = '\0';
80                 (*udata.str) = (*udata.str)[0..$-1];
81             }
82             return 0;
83         },
84         &cb
85     )) {
86         return true;
87     }
88 
89     ImVec2 min, max;
90     igGetItemRectMin(&min);
91     igGetItemRectMax(&max);
92 
93     auto rect = SDL_Rect(
94         cast(int)min.x+32, 
95         cast(int)min.y, 
96         cast(int)max.x, 
97         32
98     );
99 
100     SDL_SetTextInputRect(&rect);
101     return false;
102 }
103 /**
104     D compatible text input
105 */
106 bool incInputText(string wId, string label, ref string buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags.None) {
107     auto available = incAvailableSpace();
108     return incInputText(wId, label, available.x, buffer, flags);
109 }
110 
111 /**
112     D compatible text input
113 */
114 bool incInputText(string wId, string label, float width, ref string buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags.None) {
115 
116     // NOTE: null strings would result in segfault, make sure it's at least just empty.
117     if (buffer.ptr is null) {
118         buffer = "";
119     }
120 
121     // Push ID
122     auto id = igGetID(wId.ptr, wId.ptr+wId.length);
123     igPushID(id);
124     scope(exit) igPopID();
125 
126     // Set desired width
127     igPushItemWidth(width);
128     scope(success) igPopItemWidth();
129 
130     // Render label
131     scope(success) {
132         igSameLine(0, igGetStyle().ItemSpacing.x);
133         igTextEx(label.ptr, label.ptr+label.length);
134     }
135 
136     // Create callback data
137     TextCallbackUserData cb;
138     cb.str = &buffer;
139 
140     // Call ImGui's input handling
141     if (igInputText(
142         "###INPUT",
143         cast(char*)buffer.ptr, 
144         buffer.length+1,
145         flags | ImGuiInputTextFlags.CallbackResize,
146         cast(ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data) {
147             TextCallbackUserData* udata = cast(TextCallbackUserData*)data.UserData;
148 
149             // Allow resizing strings on GC heap
150             if (data.EventFlag == ImGuiInputTextFlags.CallbackResize) {
151 
152                 // Make sure the buffer doesn't become negatively sized.
153                 if (data.BufTextLen < 0) data.BufTextLen = 0;
154             
155                 // Resize and pass buffer ptr in
156                 (*udata.str).length = data.BufTextLen+1;
157 
158                 // slice out the null terminator
159                 data.Buf = cast(char*)(*udata.str).ptr;
160                 data.Buf[data.BufTextLen] = '\0';
161                 (*udata.str) = (*udata.str)[0..$-1];
162             }
163             return 0;
164         },
165         &cb
166     )) {
167         return true;
168     }
169 
170     ImVec2 min, max;
171     igGetItemRectMin(&min);
172     igGetItemRectMax(&max);
173 
174     auto rect = SDL_Rect(
175         cast(int)min.x+32, 
176         cast(int)min.y, 
177         cast(int)max.x, 
178         32
179     );
180 
181     SDL_SetTextInputRect(&rect);
182     return false;
183 }