1 /*
2     Copyright © 2022, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.widgets.button;
8 import creator.widgets;
9 import creator.core;
10 import inochi2d;
11 import std.math : isFinite;
12 import std.string;
13 
14 bool incButtonColored(const(char)* text, const ImVec2 size, const ImVec4 textColor = ImVec4(float.nan, float.nan, float.nan, float.nan)) {
15     if (!isFinite(textColor.x) || !isFinite(textColor.y) || !isFinite(textColor.z) || !isFinite(textColor.w)) {
16         return igButton(text, size);
17     }
18 
19     igPushStyleColor(ImGuiCol.Text, textColor);
20     scope(exit) igPopStyleColor();
21 
22     return igButton(text, size);
23 }
24 
25 bool incDropdownButtonIcon(string idStr, string icon, ImVec2 size = ImVec2(-1, -1), bool open=false) {
26     
27     // Early escape to incDropdownButton if no icon is set
28     if (icon.length == 0) return incDropdownButton(idStr, size, open);
29 
30     // Otherwise we begin our special code path
31     auto ctx = igGetCurrentContext();
32     auto window = igGetCurrentWindow();
33     auto style = igGetStyle();
34 
35     if (window.SkipItems)
36         return false;
37     
38     // Should always appear on same line
39     igSameLine(0, 0);
40     ImVec2 isize = incMeasureString(icon);
41 
42     const(float) default_size = igGetFrameHeight();
43     if (size.x <= 0) size.x = isize.x+28;
44     if (size.y <= 0) size.y = default_size;
45 
46     auto id = igGetID(idStr.ptr, idStr.ptr+idStr.length);
47     const(ImRect) bb = {
48         window.DC.CursorPos,
49         ImVec2(window.DC.CursorPos.x + size.x, window.DC.CursorPos.y + size.y)
50     };
51 
52     igItemSize(size, (size.y >= default_size) ? ctx.Style.FramePadding.y : -1.0f);
53     if (!igItemAdd(bb, id))
54         return false;
55 
56     bool hovered, held;
57     bool pressed = igButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags.None);
58 
59     // Render
60     const ImU32 bgCol = igGetColorU32(
61         ((held && hovered) || open) ? style.Colors[ImGuiCol.ButtonActive] : hovered ? 
62             style.Colors[ImGuiCol.ButtonHovered] : 
63             style.Colors[ImGuiCol.Button]);
64     igRenderNavHighlight(bb, id);
65     igRenderFrame(bb.Min, bb.Max, bgCol, true, ctx.Style.FrameRounding);
66     string s = "";
67     ImVec2 ssize = incMeasureString(s);
68     
69     igRenderText(ImVec2(
70         bb.Min.x + max(0.0f, 4), 
71         bb.Min.y + max(0.0f, (size.y - isize.y) * 0.5f)
72     ), icon.ptr, icon.ptr+icon.length, true);
73     
74     igRenderText(ImVec2(
75         bb.Min.x + max(0.0f, (size.x - (ssize.x+2))), 
76         bb.Min.y + max(0.0f, (size.y - ssize.y) * 0.5f)
77     ), s.ptr, s.ptr+s.length, true);
78     return pressed;
79 }
80 
81 bool incDropdownButton(string idStr, ImVec2 size = ImVec2(-1, -1), bool open=false) {
82     auto ctx = igGetCurrentContext();
83     auto window = igGetCurrentWindow();
84     auto style = igGetStyle();
85 
86     if (window.SkipItems)
87         return false;
88     
89     // Should always appear on same line
90     igSameLine(0, 0);
91 
92     const(float) default_size = igGetFrameHeight();
93     if (size.x <= 0) size.x = 16;
94     if (size.y <= 0) size.y = default_size;
95 
96     auto id = igGetID(idStr.ptr, idStr.ptr+idStr.length);
97     const(ImRect) bb = {
98         window.DC.CursorPos,
99         ImVec2(window.DC.CursorPos.x + size.x, window.DC.CursorPos.y + size.y)
100     };
101 
102     igItemSize(size, (size.y >= default_size) ? ctx.Style.FramePadding.y : -1.0f);
103     if (!igItemAdd(bb, id))
104         return false;
105 
106     bool hovered, held;
107     bool pressed = igButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags.None);
108 
109     // Render
110     const ImU32 bgCol = igGetColorU32(
111         ((held && hovered) || open) ? style.Colors[ImGuiCol.ButtonActive] : hovered ? 
112             style.Colors[ImGuiCol.ButtonHovered] : 
113             style.Colors[ImGuiCol.Button]);
114     igRenderNavHighlight(bb, id);
115     igRenderFrame(bb.Min, bb.Max, bgCol, true, ctx.Style.FrameRounding);
116     const(string) s = "";
117     ImVec2 ssize = incMeasureString(s);
118 
119     igRenderText(ImVec2(
120         bb.Min.x + max(0.0f, (size.x - ssize.x) * 0.5f), 
121         bb.Min.y + max(0.0f, (size.y - ssize.y) * 0.5f)
122     ), s.ptr, s.ptr+s.length, true);
123     return pressed;
124 }
125 
126 private {
127     struct DropDownMenuData {
128         bool wasOpen;
129         ImVec2 winSize;
130     }
131 }
132 
133 bool incBeginDropdownMenu(string idStr, string icon="") {
134     auto storage = igGetStateStorage();
135     auto window = igGetCurrentWindow();
136     auto id = igGetID(idStr.ptr, idStr.ptr+idStr.length);
137 
138     igPushID(id);
139     DropDownMenuData* menuData = cast(DropDownMenuData*)ImGuiStorage_GetVoidPtr(storage, igGetID("WAS_OPEN"));
140     if (!menuData) {
141         menuData = cast(DropDownMenuData*)igMemAlloc(DropDownMenuData.sizeof);
142         ImGuiStorage_SetVoidPtr(storage, igGetID("WAS_OPEN"), menuData);
143     }
144 
145     // Dropdown button itself
146     auto pressed = incDropdownButtonIcon("DROPDOWN_BTN", icon, ImVec2(-1, -1), menuData.wasOpen);
147     if (igIsPopupOpen("DROPDOWN_CONTENT") && pressed) igClosePopupsOverWindow(window, true);
148     else if (pressed) igOpenPopup("DROPDOWN_CONTENT", ImGuiPopupFlags.MouseButtonLeft | ImGuiPopupFlags.NoOpenOverItems);
149 
150     ImVec2 pos;
151     igGetCursorScreenPos(&pos);
152 
153     // Clamp to outer window
154     if (window) pos.x = clamp(pos.x, window.OuterRectClipped.Max.x, window.OuterRectClipped.Min.x-192);
155 
156     // Dropdown menu
157     igSetNextWindowSizeConstraints(ImVec2(192, 0), ImVec2(192, float.max));
158     igSetNextWindowPos(ImVec2(pos.x, pos.y+4), ImGuiCond.Always, ImVec2(0, 0));
159     menuData.wasOpen = igBeginPopup("DROPDOWN_CONTENT");
160     if (!menuData.wasOpen) igPopID();
161     else {
162         menuData.winSize = igGetCurrentWindow().Size;
163     }
164     return menuData.wasOpen;
165 }
166 
167 void incEndDropdownMenu() {
168     igEndPopup();
169     igPopID();
170 }