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 }