summaryrefslogtreecommitdiff
path: root/src/imgui_ui.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imgui_ui.cpp')
-rw-r--r--src/imgui_ui.cpp1502
1 files changed, 1502 insertions, 0 deletions
diff --git a/src/imgui_ui.cpp b/src/imgui_ui.cpp
new file mode 100644
index 0000000..612ee1a
--- /dev/null
+++ b/src/imgui_ui.cpp
@@ -0,0 +1,1502 @@
+
+#include "imgui_internal_widgets.h"
+
+#include "imgui_ops.h"
+
+#include "imgui_helper.cpp"
+
+#include "imgui_ui_properties.cpp"
+#include "imgui_ui_timeline.cpp"
+
+#if DEBUG
+#include "imgui_ui_debug.cpp"
+#endif
+#if STABLE
+#include "imgui_ui_stable_diffusion.cpp"
+#endif
+
+static void
+ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io,
+ sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray)
+{
+ ImGui::Begin("Files");
+ ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
+
+ if (ImGui::IsKeyPressed(ImGuiKey_Backspace)) {
+ // TODO(fox): Delete sources code!
+
+ /*
+ uint64 SortSize = (sizeof(uint16) * File->Comp_Count);
+ void *SortedArray = Memory_PushScratch(Memory, SortSize);
+ uint16 *SelectedSourceIndex = (uint16 *)SortedArray;
+ int SelectedSourceCount = 0;
+
+ int h = 0, c = 0, i = 0;
+ int SourceCount = File->Source_Count;
+ while (Block_Loop(Memory, F_Sources, SourceCount, &h, &c, &i)) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i);
+ if (Source->IsSelected) {
+ SelectedSourceIndex[SelectedSourceCount] = i;
+ SelectedSourceCount++;
+ }
+ }
+
+ h = 0, c = 0, i = 0;
+ int LayerCount = File->Layer_Count;
+ while (Block_Loop(Memory, F_Layers, LayerCount, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ for (int b = 0; b < SelectedSourceCount; b++) {
+ if (SelectedSourceIndex[b] == Layer->Block_Source_Index) {
+ }
+ }
+ }
+
+ Memory_PopScratch(Memory, SortSize);
+ */
+
+ /*
+ bool32 CommitAction = 0;
+ while (Block_Loop(Memory, F_Sources, SourceCount, &h, &c, &i)) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i);
+ if (Source->IsSelected) {
+ if (!CommitAction) {
+ History_Entry_Commit(Memory, "Delete source");
+ CommitAction = 1;
+ }
+ Source_Delete(File, Memory, i);
+ }
+ }
+ if (CommitAction)
+ History_Entry_End(Memory);
+ */
+ }
+
+ for (int c = 0; c < File->Comp_Count; c++) {
+ block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, c);
+ block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Comp->Name_String_Index);
+ ImGui::Text(String->Char);
+ }
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Sources, File->Source_Count, &h, &c, &i)) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, i);
+ block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index);
+ ImGui::Selectable(String->Char, Source->IsSelected);
+ if (ImGui::IsItemClicked() || ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
+ if (!io.KeyShift && !Source->IsSelected) {
+ Source_DeselectAll(File, Memory);
+ }
+ Source->IsSelected = 1;
+ }
+ ImGui::OpenPopupOnItemClick("sourcecontext", ImGuiPopupFlags_MouseButtonRight);
+ }
+
+ if (ImGui::BeginPopup("sourcecontext")) {
+ if (ImGui::MenuItem("Create layer from source")) {
+ State->HotkeyInput = hotkey_newlayerfromsource;
+ }
+ ImGui::EndPopup();
+ }
+
+ // if (!ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
+ // Source_DeselectAll(File, Memory);
+
+#if DEBUG
+
+ for (int i = 0; i < Debug.Temp.WatchedProperties; i++) {
+ if (Debug.Temp.DebugPropertyType[i] == d_float) {
+ ImGui::Text("%s: %f", Debug.Temp.String[i], Debug.Temp.Val[i].f);
+ } else if (Debug.Temp.DebugPropertyType[i] == d_int) {
+ ImGui::Text("%s: %i", Debug.Temp.String[i], Debug.Temp.Val[i].i);
+ } else if (Debug.Temp.DebugPropertyType[i] == d_uint) {
+ ImGui::Text("%s: %u", Debug.Temp.String[i], Debug.Temp.Val[i].u);
+ }
+ }
+#endif
+ ImGui::End();
+}
+
+static void
+ImGui_ColorPanel(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io)
+{
+ ImGuiStyle& style = ImGui::GetStyle();
+
+ ImGui::Begin("Colors");
+
+ ImGuiColorEditFlags flags_primary = ImGuiColorEditFlags_AlphaPreview |
+ ImGuiColorEditFlags_Float;
+ ImGuiColorEditFlags flags_picker = ImGuiColorEditFlags_PickerHueBar |
+ ImGuiColorEditFlags_AlphaBar |
+ ImGuiColorEditFlags_NoSmallPreview |
+ ImGuiColorEditFlags_NoSidePreview |
+ ImGuiColorEditFlags_DisplayRGB |
+ ImGuiColorEditFlags_DisplayHSV |
+ ImGuiColorEditFlags_DisplayHex;
+
+ // Dim window if it's not active so there's not a big saturation square in
+ // the corner of my vision while I'm editing. Personal preference.
+ real32 AlphaMult = (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) ? 1.0f : 0.3f;
+ ImGui::PushStyleVar(ImGuiStyleVar_Alpha, style.Alpha * AlphaMult);
+
+ ImGui::ColorPicker4("##maincolorpicker", &UI->Color.r, flags_primary | flags_picker);
+
+ ImGui::PopStyleVar();
+
+ if (ImGui::ColorButton("##primarycolor", *(ImVec4*)&UI->Color.r, flags_primary, ImVec2(20, 20)))
+ {
+ v4 Temp = UI->Color;
+ UI->Color = UI->AltColor;
+ UI->AltColor = Temp;
+ }
+ if (ImGui::ColorButton("##secondarycolor", *(ImVec4*)&UI->AltColor.r, flags_primary, ImVec2(20, 20)))
+ {
+ v4 Temp = UI->Color;
+ UI->Color = UI->AltColor;
+ UI->AltColor = Temp;
+ }
+
+ if (State->Tool == tool_brush) {
+ real32 BrushSizeMin = 0;
+ real32 BrushSizeMax = 1024;
+ real32 BrushHardnessMin = 0.5;
+ real32 BrushHardnessMax = 100;
+ real32 BrushSpacingMin = 0.1;
+ real32 BrushSpacingMax = 100;
+ if (ImGui::DragScalar("Size", ImGuiDataType_Float, &State->Brush.Size, 1, &BrushSizeMin, &BrushSizeMax, "%.3f")) {
+ Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4);
+ State_BindBrushTexture(Memory, &State->Brush, 4);
+ }
+ if (ImGui::DragScalar("Hardness", ImGuiDataType_Float, &State->Brush.Hardness, 1, &BrushHardnessMin, &BrushHardnessMax, "%.3f", ImGuiSliderFlags_Logarithmic)) {
+ Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4);
+ State_BindBrushTexture(Memory, &State->Brush, 4);
+ }
+ if (ImGui::DragScalar("Spacing", ImGuiDataType_Float, &State->Brush.Spacing, 1, &BrushSpacingMin, &BrushSpacingMax, "%.3f", ImGuiSliderFlags_Logarithmic)) {
+ Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4);
+ State_BindBrushTexture(Memory, &State->Brush, 4);
+ }
+ }
+
+ ImGui::End();
+}
+
+static void
+ImGui_Viewport_Toolbar(project_state *State, ImDrawList *draw_list)
+{
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
+ // ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(50, 50, 50, 0));
+
+ real32 IconSize = ImGui::GetFontSize() * 4;
+ int ToolCount = (int)tool_count;
+ ImVec2 ButtonSize(IconSize, IconSize);
+ ImVec2 WindowSize(IconSize, IconSize * ToolCount);
+ ImGui::BeginChild("Toolbar", WindowSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
+ for (int i = 0; i < ToolCount; i++) {
+ ImGui::PushID(i);
+ // draw_list->AddImage((void *)(intptr_t)State->ToolIconTex[i], Min, Max);
+ if ((int)State->Tool == i) {
+ ImVec2 Min = ImGui::GetCursorScreenPos();
+ ImVec2 Max = Min + ButtonSize;
+ draw_list->AddRectFilled(Min, Max, IM_COL32(255, 255, 255, 128));
+ }
+ ImGui::Button(ToolName[i], ButtonSize);
+ if (ImGui::IsItemActivated()) {
+ State->Tool = (tool)i;
+ }
+ ImGui::PopID();
+ }
+ ImGui::EndChild();
+
+ // ImGui::PopStyleColor();
+ ImGui::PopStyleVar(2);
+}
+
+static void
+ImGui_Viewport_BrushUI(project_state *State, memory *Memory, ImVec2 ViewportMin, ImVec2 ViewportMax, ImVec2 CompZoom, ImGuiIO io, uint16 Width, uint16 Height)
+{
+
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ if (State->Tool == tool_brush) {
+
+ if (ImGui::IsKeyPressed(ImGuiKey_ModAlt, false)) {
+ State->Brush.UIPos = io.MousePos;
+ }
+
+ ImVec2 CompScale = CompZoom / ImVec2(Width, Height);
+ ImVec2 BrushSize = CompScale * State->Brush.Size;
+ ImVec2 MinBounds = State->Brush.UIPos - BrushSize/2;
+ ImVec2 MaxBounds = MinBounds + BrushSize;
+
+ // if (io.KeyCtrl) {
+ // ImGui::SetCursorScreenPos(State->Brush.UIPos);
+ // char buf[256];
+ // sprintf(buf, "RGBA: %.1f, %.1f, %.1f, %.1f", State->Brush.Size, State->Brush.Hardness);
+ // }
+
+ if (io.KeyAlt) {
+ draw_list->PushClipRect(ViewportMin, ViewportMax, true);
+ draw_list->AddImage((void *)(intptr_t)State->Brush.GLTexture, MinBounds, MaxBounds, ImVec2(0, 0), ImVec2(1, 1), 1);
+ draw_list->PopClipRect();
+ ImGui::SetCursorScreenPos(State->Brush.UIPos);
+ char buf[256];
+ sprintf(buf, "Size: %.1f, Hardness: %.1f", State->Brush.Size, State->Brush.Hardness);
+ ImGui::Text(buf);
+ if (io.MouseDelta.x || io.MouseDelta.y) {
+ ImVec2 Delta = io.MouseDelta;
+ State->Brush.Size += Delta.x;
+ State->Brush.Hardness += Delta.y*State->Brush.Hardness/100;
+ if (State->Brush.Size < 0)
+ State->Brush.Size = 0;
+ if (State->Brush.Size > 1024)
+ State->Brush.Size = 1024;
+ if (State->Brush.Hardness < 0.5)
+ State->Brush.Hardness = 0.5;
+ if (State->Brush.Hardness > 100)
+ State->Brush.Hardness = 100;
+ Brush_CalcBitmapAlphaFromSize(Memory, &State->Brush, 4);
+ State_BindBrushTexture(Memory, &State->Brush, 4);
+ }
+ }
+ }
+}
+
+static void
+ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDrawList *draw_list, ImGuiIO &io,
+ interact_transform *Interact, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray)
+{
+ v2 InteractMin = Interact->Min + Interact->Position;
+ v2 InteractMax = Interact->Max + Interact->Position;
+
+ v2 BoxLength = InteractMax - InteractMin;
+ v2 Center = InteractMax - (BoxLength/2);
+
+ real32 Point0X = Center.x - InteractMin.x;
+ real32 Point0Y = Center.y - InteractMin.y;
+
+ real32 Rad = Interact->Radians;
+
+ v2 XAxis = (Point0X * Interact->Scale)*V2(cos(Rad), sin(Rad));
+ v2 YAxis = (Point0Y * -Interact->Scale)*V2(sin(Rad), -cos(Rad));
+
+ // Points are clockwise starting from the top left.
+ real32 X0 = -XAxis.x - YAxis.x + Center.x;
+ real32 Y0 = -XAxis.y - YAxis.y + Center.y;
+ real32 X1 = X0 + XAxis.x*2;
+ real32 Y1 = Y0 + XAxis.y*2;
+ real32 X2 = X1 + YAxis.x*2;
+ real32 Y2 = Y1 + YAxis.y*2;
+ real32 X3 = X2 - XAxis.x*2;
+ real32 Y3 = Y2 - XAxis.y*2;
+
+ // Midway points.
+ real32 Mid_X0 = X0 + XAxis.x;
+ real32 Mid_Y0 = Y0 + XAxis.y;
+ real32 Mid_X1 = X1 + YAxis.x;
+ real32 Mid_Y1 = Y1 + YAxis.y;
+ real32 Mid_X2 = X2 - XAxis.x;
+ real32 Mid_Y2 = Y2 - XAxis.y;
+ real32 Mid_X3 = X3 - YAxis.x;
+ real32 Mid_Y3 = Y3 - YAxis.y;
+
+ ImVec2 CompScale = UI->CompZoom / ImVec2(CompWidth, CompHeight);
+
+ ImVec2 P[4];
+ P[0] = ImVec2(X0, Y0)*CompScale + UI->CompPos;
+ P[1] = ImVec2(X1, Y1)*CompScale + UI->CompPos;
+ P[2] = ImVec2(X2, Y2)*CompScale + UI->CompPos;
+ P[3] = ImVec2(X3, Y3)*CompScale + UI->CompPos;
+
+ ImVec2 Mid_P[4];
+ Mid_P[0] = ImVec2(Mid_X0, Mid_Y0)*CompScale + UI->CompPos;
+ Mid_P[1] = ImVec2(Mid_X1, Mid_Y1)*CompScale + UI->CompPos;
+ Mid_P[2] = ImVec2(Mid_X2, Mid_Y2)*CompScale + UI->CompPos;
+ Mid_P[3] = ImVec2(Mid_X3, Mid_Y3)*CompScale + UI->CompPos;
+
+ ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text);
+ draw_list->AddLine(P[0], P[1], wcol, 2.0f);
+ draw_list->AddLine(P[1], P[2], wcol, 2.0f);
+ draw_list->AddLine(P[2], P[3], wcol, 2.0f);
+ draw_list->AddLine(P[3], P[0], wcol, 2.0f);
+
+ v2 XAxis2 = (BoxLength*CompScale.x)*V2(cos(Rad), sin(Rad));
+ v2 YAxis2 = (BoxLength*CompScale.y)*V2(sin(Rad), -cos(Rad));
+
+ v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis;
+ v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis;
+
+ // real32 LocalX = ((io.MousePos.x - UI->CompPos.x) - Center.x) ;
+ // real32 LocalY = ((io.MousePos.y - UI->CompPos.y) - Center.y) ;
+ layer_transforms BoxTransforms = { Center.x, Center.y, 0.5, 0.5, (real32)(Interact->Radians / (PI / 180)), Interact->Scale };
+ v2 LayerPoint = Transform_ScreenSpaceToLocal(BoxTransforms, CompWidth, CompHeight, BoxLength.x, BoxLength.y, UI->CompPos, UI->CompZoom, ViewportMin, io.MousePos);
+
+ real32 U = LayerPoint.x / BoxLength.x;
+ real32 V = LayerPoint.y / BoxLength.y;
+
+ ImVec2 ScaleHandleSize(50, 50);
+
+ bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z);
+
+ // First do the halfway scale points, since they don't need UVs considered:
+ for (int i = 0; i < 4; i++) {
+ ImGui::SetCursorScreenPos(Mid_P[i] - ScaleHandleSize/2);
+ ImGui::PushID(i);
+
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f)));
+ ImGui::Button("##ScaleMids", ScaleHandleSize);
+ ImGui::PopStyleColor();
+
+ if (ImGui::IsItemActivated() && !OtherActions) {
+ State->InteractTransformMode = 1;
+ }
+
+ if (State->InteractTransformMode == 1 && ImGui::IsItemActive())
+ {
+ uint32 side = i;
+ if (side == 0) {
+ Interact->Scale -= io.MouseDelta.y / BoxLength.y;
+ Interact->Position.y += io.MouseDelta.y / 2;
+ } else if (side == 1) {
+ Interact->Scale += io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ } else if (side == 2) {
+ Interact->Scale += io.MouseDelta.y / BoxLength.y;
+ Interact->Position.y += io.MouseDelta.y / 2;
+ } else if (side == 3) {
+ Interact->Scale -= io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ }
+ }
+ ImGui::PopID();
+ }
+
+ bool32 InBounds = false;
+ // Scale if cursor is on button within the UV, rotate if outside UV, and position if a non-button is dragged.
+ if (U >= 0.0f && U <= 1.0f && V >= 0.0f && V <= 1.0f)
+ {
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f)));
+ InBounds = true;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ ImGui::SetCursorScreenPos(P[i] - ScaleHandleSize/2);
+ ImGui::PushID(i);
+ ImGui::Button("##ScaleRotateCorners", ScaleHandleSize);
+
+ if (ImGui::IsItemActivated() && !OtherActions) {
+ if (InBounds)
+ State->InteractTransformMode = 1;
+ else
+ State->InteractTransformMode = 2;
+ }
+
+ // Scale part
+ if (State->InteractTransformMode == 1 && ImGui::IsItemActive())
+ {
+ // TODO(fox): Corner dragging scale only works in the X
+ // axis. Mostly feels right when dragged how you expect,
+ // but I'll fix it if someone complains.
+ uint32 side = i;
+ if (side == 0) {
+ Interact->Scale -= io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ } else if (side == 1) {
+ Interact->Scale += io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ } else if (side == 2) {
+ Interact->Scale += io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ } else if (side == 3) {
+ Interact->Scale -= io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ }
+ }
+
+ // Rotation part
+ if (State->InteractTransformMode == 2 && ImGui::IsItemActive())
+ {
+ real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2);
+ real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2);
+
+ real32 Slope_Mouse = LocalY/LocalX;
+ real32 Slope_Corner = 0;
+ real32 Slope_Flipped = 0;
+ real32 Dot = 0;
+
+ // TODO(fox) learn basic geometry to do this properly
+
+ // We find the angle between the direction of whichever corner the
+ // mouse is grabbing (Slope_Corner) and the mouse's current
+ // position (Slope_Mouse) to get ExtraRadians. The calculation only
+ // works between -90 and 90, so I take the dot product of the
+ // opposite edge of the corner and add the extra degrees when it's negative.
+
+ v2 SlopeDot = V2(BoxLength.x, BoxLength.y);
+ // top left clockwise
+ uint32 side = i;
+ if (side == 0) {
+ Slope_Corner = BoxLength.y / BoxLength.x;
+ Slope_Flipped = -BoxLength.x / BoxLength.y;
+ Dot = LocalX * -SlopeDot.x + LocalY * -SlopeDot.y;
+ } else if (side == 1) {
+ Slope_Corner = -BoxLength.y / BoxLength.x;
+ Slope_Flipped = BoxLength.x / BoxLength.y;
+ Dot = LocalX * SlopeDot.x + LocalY * -SlopeDot.y;
+ } else if (side == 2) {
+ Slope_Corner = BoxLength.y / BoxLength.x;
+ Slope_Flipped = -BoxLength.x / BoxLength.y;
+ Dot = LocalX * SlopeDot.x + LocalY * SlopeDot.y;
+ } else if (side == 3) {
+ Slope_Corner = -BoxLength.y / BoxLength.x;
+ Slope_Flipped = BoxLength.x / BoxLength.y;
+ Dot = LocalX * -SlopeDot.x + LocalY * SlopeDot.y;
+ }
+
+ Interact->Radians = atan((Slope_Mouse - Slope_Corner) / (1 + Slope_Mouse * Slope_Corner));
+ real32 ExtraRadians2 = atan((Slope_Mouse - Slope_Flipped) / (1 + Slope_Mouse * Slope_Flipped));
+
+ if (Dot < 0) {
+ if (Interact->Radians < 0) {
+ Interact->Radians = (90 * (PI / 180)) + ExtraRadians2;
+ } else {
+ Interact->Radians = (-90 * (PI / 180)) + ExtraRadians2;
+ }
+ }
+ }
+
+ ImGui::PopID();
+ }
+
+ if (!State->InteractTransformMode && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && InBounds && !OtherActions)
+ State->InteractTransformMode = 3;
+
+ if (State->InteractTransformMode == 3) {
+ Interact->Position.x += (real32)io.MouseDelta.x/CompScale.x;
+ Interact->Position.y += (real32)io.MouseDelta.y/CompScale.y;
+ }
+
+ if (State->InteractTransformMode)
+ {
+ if (io.MouseDelta.x || io.MouseDelta.y)
+ State->UpdateFrame = true;
+ if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
+ State->InteractTransformMode = 0;
+ }
+
+ if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
+ State->Interact_Active = interact_type_none;
+ State->Interact_Modifier = 0;
+ State->UpdateFrame = true;
+ }
+
+ // Second condition so you don't have to reach for Enter.
+ if (ImGui::IsKeyPressed(ImGuiKey_Enter) || (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && io.KeyCtrl)) {
+ int h = 0, c = 0, i = 0;
+ if (!io.KeyCtrl)
+ History_Entry_Commit(Memory, "Transform layers");
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected == 1) {
+ if (io.KeyCtrl) {
+ layer_transforms T = Layer_GetTransforms(Layer);
+ Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
+ property_channel *Property[4] = { &Layer->x, &Layer->y, &Layer->rotation, &Layer->scale };
+ real32 Val[4] = { T.x, T.y, T.rotation, T.scale };
+ for (int a = 0; a < 4; a++) {
+ if (Property[a]->CurrentValue != Val[a]) {
+ History_Entry_Commit(Memory, "Add keyframe");
+ bezier_point Point = { 1, {(real32)State->Frame_Current, Val[a], -1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 };
+ uint16 *ArrayLocation = Property_GetSortedArray(SortedKeyframeArray, State->MostRecentlySelectedLayer, h);
+ Bezier_Add(Memory, F_Layers, Property[a], Point, ArrayLocation);
+ History_Entry_End(Memory);
+ }
+ }
+ } else {
+ History_Action_Swap(Memory, F_File, sizeof(Layer->x.CurrentValue), &Layer->x.CurrentValue);
+ History_Action_Swap(Memory, F_File, sizeof(Layer->y.CurrentValue), &Layer->y.CurrentValue);
+ History_Action_Swap(Memory, F_File, sizeof(Layer->scale.CurrentValue), &Layer->scale.CurrentValue);
+ History_Action_Swap(Memory, F_File, sizeof(Layer->rotation.CurrentValue), &Layer->rotation.CurrentValue);
+ Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue);
+ }
+ }
+ }
+ if (!io.KeyCtrl)
+ History_Entry_End(Memory);
+ State->Interact_Active = interact_type_none;
+ State->Interact_Modifier = 0;
+ State->UpdateFrame = true;
+ }
+
+ if (InBounds == true) {
+ ImGui::PopStyleColor();
+ }
+
+}
+
+static void
+ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions,
+ sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray)
+{
+ sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex];
+ sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex);
+ ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text);
+ for (int i = 0; i < SortedCompStart->LayerCount; i++)
+ {
+ sorted_layer_array SortEntry = SortedLayerStart[i];
+ uint32 Index_Physical = SortEntry.Block_Layer_Index;
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
+ if (Layer->IsPrecomp) {
+ ParentLayer[Recursions] = Layer;
+ ImGui_Viewport_SelectedLayerUI(State, Memory, UI, draw_list, MainComp, Layer->Block_Source_Index, ParentLayer, Recursions + 1, SortedCompArray, SortedLayerArray);
+ }
+ if (Layer->IsSelected) {
+ uint32 Width = 0, Height = 0;
+ if (!Layer->IsPrecomp) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
+ Width = Source->Width;
+ Height = Source->Height;
+ } else {
+ block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index);
+ Width = Comp->Width;
+ Height = Comp->Height;
+ }
+
+ v2 Point[5] = { V2(Width*Layer->ax.CurrentValue, Height*Layer->ay.CurrentValue), V2(0, 0), V2(Width, 0), V2(0, Height), V2(Width, Height) };
+
+ layer_transforms T = Layer_GetTransforms(Layer);
+
+ if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) {
+ Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
+ }
+
+ v2 NewPos[5];
+ for (int i = 0; i < 5; i++) {
+ NewPos[i] = TransformPoint(T, Width, Height, Point[i]);
+ }
+
+ int i = 0;
+ while (i < Recursions) {
+ T = Layer_GetTransforms(ParentLayer[i]);
+ block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, ParentLayer[i]->Block_Source_Index);
+ Width = Comp->Width;
+ Height = Comp->Height;
+ for (int i = 0; i < 5; i++) {
+ NewPos[i] = TransformPoint(T, Width, Height, NewPos[i]);
+ }
+ i++;
+ }
+
+ ImVec2 ScreenPoint[5];
+ for (int i = 0; i < 5; i++) {
+ v2 CompUV = NewPos[i] / V2(MainComp->Width, MainComp->Height);
+
+ ScreenPoint[i] = ImVec2(UI->CompPos.x + CompUV.x * UI->CompZoom.x,
+ UI->CompPos.y + CompUV.y * UI->CompZoom.y);
+
+ }
+ if (State->Tool != tool_brush) {
+ ImU32 wcol2 = IM_COL32(10, 10, 10, 255);
+ draw_list->AddNgon(ScreenPoint[0], 10, wcol2, 8, 9.0f);
+ draw_list->AddNgon(ScreenPoint[0], 10, wcol, 8, 5.0f);
+ }
+ draw_list->AddLine(ScreenPoint[1], ScreenPoint[2], wcol, 2.0f);
+ draw_list->AddLine(ScreenPoint[2], ScreenPoint[4], wcol, 2.0f);
+ draw_list->AddLine(ScreenPoint[1], ScreenPoint[3], wcol, 2.0f);
+ draw_list->AddLine(ScreenPoint[3], ScreenPoint[4], wcol, 2.0f);
+ }
+ }
+}
+
+static void
+ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, GLuint textureID,
+ sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, uint16 *SortedKeyframeArray)
+{
+ bool open = true;
+ ImGui::Begin("Viewport", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
+
+ if (ImGui::IsWindowHovered(ImGuiFocusedFlags_ChildWindows)) {
+ State->FocusedWindow = focus_viewport;
+ }
+
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+
+ ImVec2 ViewportMin = ImGui::GetCursorScreenPos();
+ ImVec2 ViewportScale = ImGui::GetContentRegionAvail();
+ ImVec2 ViewportMax = ImVec2(ViewportMin.x + ViewportScale.x, ViewportMin.y + ViewportScale.y);
+
+ if (State->Initializing) {
+ UI->CompZoom = ImVec2(MainComp->Width, MainComp->Height);
+ UI->CompPos = ImVec2(ViewportMin.x + ((ViewportMax.x - ViewportMin.x)/2 - UI->CompZoom.x/2),
+ ViewportMin.y + ((ViewportMax.y - ViewportMin.y)/2 - UI->CompZoom.y/2));
+ }
+
+ ImVec2 CompPosMin = ImVec2(UI->CompPos.x, UI->CompPos.y);
+ ImVec2 CompPosMax = ImVec2(UI->CompPos.x + UI->CompZoom.x, UI->CompPos.y + UI->CompZoom.y);
+
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ draw_list->AddRectFilled(ViewportMin, ViewportMax, IM_COL32(50, 50, 50, 255));
+ draw_list->AddRect(ViewportMin, ViewportMax, IM_COL32(255, 255, 255, 255));
+ draw_list->AddRect(CompPosMin, CompPosMax, IM_COL32(255, 255, 255, 55));
+
+ real32 FontSize = ImGui::GetFontSize();
+ ImGui::SetCursorScreenPos(ImVec2(ViewportMax.x - FontSize*2, ViewportMin.y + ViewportScale.y - FontSize*3.0));
+ ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 80));
+ if (ImGui::Button("?"))
+ State->ImGuiPopups = popup_keybinds;
+ ImGui::PopStyleColor();
+
+
+ // Actual composition texture
+ draw_list->PushClipRect(ViewportMin, ViewportMax, true);
+ draw_list->AddImage((void *)(intptr_t)textureID, CompPosMin, CompPosMax);
+ draw_list->PopClipRect();
+
+ // UI+interaction for layer
+ if (State->MostRecentlySelectedLayer > -1)
+ {
+ block_layer *ParentLayer[4];
+ ImGui_Viewport_SelectedLayerUI(State, Memory, UI, draw_list, MainComp, File->PrincipalCompIndex, ParentLayer, 0, SortedCompArray, SortedLayerArray);
+ if (State->Interact_Active == interact_type_viewport_transform) {
+ ImGui_Viewport_TransformUI(File, State, Memory, UI, draw_list, io, (interact_transform *)&State->Interact_Offset[0], ViewportMin, MainComp->Width, MainComp->Height, SortedKeyframeArray);
+ }
+ }
+
+
+
+ // Interactions for dragging and zooming
+ ImGui::SetCursorScreenPos(ViewportMin);
+
+ ImGui::InvisibleButton("canvas", ViewportScale, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
+ bool32 IsHovered = ImGui::IsItemHovered();
+#if 1
+ bool32 IsActive = ImGui::IsItemActive();
+ bool32 IsActivated = ImGui::IsItemActivated();
+ bool32 IsDeactivated = ImGui::IsItemDeactivated();
+#else
+ bool32 IsActive = ImGui::IsKeyDown(ImGuiKey_3);
+ bool32 IsActivated = ImGui::IsKeyPressed(ImGuiKey_3);
+ bool32 IsDeactivated = ImGui::IsKeyReleased(ImGuiKey_3);
+#endif
+
+ if (IsHovered && IsActivated && !ImGui::IsMouseDown(ImGuiMouseButton_Right))
+ {
+ // Point to zoom in on if Z is held
+ State->TempZoomRatio = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos);
+
+ if (State->Tool == tool_brush && State->Interact_Active != interact_type_brush && !ImGui::IsKeyDown(ImGuiKey_Z)) {
+ if (!io.KeyCtrl) {
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i))
+ {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (!Layer->IsPrecomp) {
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
+ if (Layer->IsSelected && Source->Type == source_type_principal) {
+ Assert(Source->BytesPerPixel == 4);
+ Arbitrary_Zero((uint8 *)State->Brush.TransientBitmap, 2048*2048*4);
+ State->Interact_Active = interact_type_brush;
+ State->Brush.LayerToPaint_Index = i;
+ break;
+ }
+ }
+ }
+ }
+ if (State->Brush.LayerToPaint_Index == -1) {
+ State->HotkeyInput = hotkey_newpaintlayer;
+ }
+ }
+
+ // Layer selection
+ if (!ImGui::IsKeyDown(ImGuiKey_Z) && State->Tool == tool_default && State->Interact_Active == interact_type_none) {
+ int32 Selection = Layer_TestSelection(Memory, State, UI, SortedCompArray, SortedLayerArray, File->PrincipalCompIndex);
+ if (!io.KeyShift && State->Interact_Active == interact_type_none)
+ Layer_DeselectAll(File, State, Memory);
+ if (Selection != -1)
+ Layer_Select(Memory, State, Selection);
+ }
+ }
+
+ /*
+ if (State->Interact_Active == interact_type_viewport_transform) {
+ interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0];
+ ImVec2 DragDelta = io.MousePos - Interact->OGPos;
+ Interact->Position = V2(DragDelta.x, DragDelta.y);
+ if (io.MouseDelta.x || io.MouseDelta.y)
+ State->UpdateFrame = true;
+ }
+ */
+
+ if (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1.0f))
+ {
+ UI->CompPos.x += io.MouseDelta.x;
+ UI->CompPos.y += io.MouseDelta.y;
+ }
+
+
+ bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z) || ImGui::IsMouseDown(ImGuiMouseButton_Right);
+ if (State->Tool == tool_brush && State->Interact_Active == interact_type_brush) {
+ Assert(State->Brush.LayerToPaint_Index != -1);
+ if (IsActive && !OtherActions) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, State->Brush.LayerToPaint_Index);
+ layer_transforms T_Layer = Layer_GetTransforms(Layer);
+ block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index);
+ ImVec2 MouseDelta = io.MouseDelta;
+ real32 Delta = MouseDelta.x + MouseDelta.y;
+ v2 PrincipalCompUV = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos);
+ v2 LayerPos = Layer_TraverseForPoint(File, State, Memory, PrincipalCompUV, SortedCompArray, SortedLayerArray);
+ if (IsActivated) {
+ RenderQueue_AddBrush(State, LayerPos);
+ } else if (Delta != 0.0f) {
+ v2 PrevPos = State->Brush.PrevPos;
+ v2 Delta = PrevPos - LayerPos;
+ real32 Dist = sqrt(LengthSq(Delta));
+ if (Dist > State->Brush.Spacing) {
+ RenderQueue_AddBrush(State, LayerPos);
+ }
+ }
+ State->UpdateFrame = true;
+ }
+
+ if (IsDeactivated) {
+ RenderQueue_AddBlit(State);
+ }
+ }
+
+ if (ImGui::IsKeyDown(ImGuiKey_Z) && ImGui::IsWindowHovered()) {
+ if (IsActive)
+ ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
+ else
+ ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
+ }
+
+ real32 Distance = 0;
+ if (IsActive) {
+ if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1.0f))
+ Distance = io.MouseDelta.x + io.MouseDelta.y;
+ if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
+ Distance = 200;
+ }
+ if (Distance && ImGui::IsKeyDown(ImGuiKey_Z))
+ {
+ if (io.KeyShift)
+ Distance *= -1;
+ UI->CompZoom.x += (Distance)*(real32)MainComp->Width/MainComp->Height;
+ UI->CompZoom.y += (Distance);
+ UI->CompPos.x -= ((Distance)*(real32)MainComp->Width/MainComp->Height)*State->TempZoomRatio.x;
+ UI->CompPos.y -= Distance*State->TempZoomRatio.y;
+ }
+
+ ImGui::SetCursorScreenPos(ImVec2(ViewportMin.x, ViewportMin.y + ViewportScale.y - FontSize*1.5));
+
+ ImGui::Text("%.1f", 100.0f * (UI->CompZoom.x / MainComp->Width));
+ if (State->MsgTime > 0) {
+ ImGui::SameLine();
+ ImGui::SetCursorPosX((ViewportScale.x / 5)*4);
+ ImGui::Text(State->Msg);
+ State->MsgTime--;
+ }
+
+ ImGui::SetCursorScreenPos(ViewportMin);
+ ImGui_Viewport_Toolbar(State, draw_list);
+ ImGui_Viewport_BrushUI(State, Memory, ViewportMin, ViewportMax, UI->CompPos, io, MainComp->Width, MainComp->Height);
+
+ ImGui::End();
+}
+
+#include "keybinds.h"
+
+static void
+ImGui_Key_GetUIInfo(key_entry KeyEntry, real32 KeySize, ImVec2 *Offset_ScreenPos, ImVec2 *KeyScreenSize) {
+ ImVec2 Extra(0, 0);
+ if (KeyEntry.Sector == 0) {
+ if (KeyEntry.Offset.x != 0) {
+ if (KeyEntry.Offset.y == 1) {
+ Extra.x += 0.5;
+ }
+ if (KeyEntry.Offset.y == 2) {
+ Extra.x += 0.75;
+ }
+ if (KeyEntry.Offset.y == 3) {
+ Extra.x += 1.5;
+ }
+ }
+ }
+ *Offset_ScreenPos = ImVec2(KeySize, KeySize) * (SectorOffset[KeyEntry.Sector] + Extra + KeyEntry.Offset);
+ *KeyScreenSize = (KeyEntry.WidthRatio > 0.0f) ? ImVec2(KeySize * KeyEntry.WidthRatio, KeySize) : ImVec2(KeySize, KeySize * -KeyEntry.WidthRatio);
+}
+
+static void
+ImGui_KeybindUI(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io)
+{
+ real32 KeySize = ImGui::GetFontSize()*2;
+ real32 KeyboardWidth = KeySize * 23.5;
+ real32 KeyboardHeight = KeySize * 7;
+ ImVec2 WindowSize = ImGui::GetWindowSize();
+ ImVec2 WindowMinAbs = ImGui::GetWindowPos();
+ ImVec2 WindowMaxAbs = WindowMinAbs + WindowSize;
+ ImVec2 KeyboardPos((WindowSize.x - KeyboardWidth) / 2, KeySize*2);
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ ImVec2 SectorOffset[4] = { ImVec2(0, 1.25), ImVec2(0,0), ImVec2(15.25, 1.25), ImVec2(19.5, 1.25) };
+
+ ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(190, 0, 50, 180));
+ if (ImGui::Button("X"))
+ ImGui::CloseCurrentPopup();
+ ImGui::PopStyleColor();
+
+ State->Split_KeybindUI.Split(draw_list, 2);
+ State->Split_KeybindUI.SetCurrentChannel(draw_list, 1);
+
+ // keyboard
+ ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 180));
+ for (int k = 0; k < AmountOf(KeyEntries); k++) {
+ key_entry KeyEntry = KeyEntries[k];
+ ImVec2 Offset_ScreenPos(0,0);
+ ImVec2 KeyScreenSize(0,0);
+ ImGui_Key_GetUIInfo(KeyEntry, KeySize, &Offset_ScreenPos, &KeyScreenSize);
+ if (KeyEntry.Name[0] != '\0') {
+ ImGui::PushID(k);
+ ImGui::SetCursorScreenPos(WindowMinAbs + KeyboardPos + Offset_ScreenPos);
+ ImGui::Button(KeyEntry.Name, KeyScreenSize);
+ ImGui::PopID();
+ }
+ }
+ ImGui::PopStyleColor();
+
+ State->Split_KeybindUI.SetCurrentChannel(draw_list, 0);
+
+ // list
+ ImVec2 SubwindowMinPos(WindowMinAbs + KeyboardPos + ImVec2(0, KeyboardHeight + KeySize));
+ ImVec2 SubwindowSize(KeyboardWidth, WindowSize.y - KeyboardHeight - KeySize*4);
+ ImGui::SetCursorScreenPos(SubwindowMinPos);
+ ImGui::BeginChild("Keybinds info", SubwindowSize);
+ key_mode CurrentKeyMode = (key_mode)9999;
+ for (int a = 0; a < AmountOf(ShortcutArray); a++) {
+ shortcut_entry ShortcutEntry = ShortcutArray[a];
+
+ // header info
+ if (CurrentKeyMode != ShortcutEntry.Mode) {
+ ImGui::Dummy(ImVec2(1, KeySize));
+ CurrentKeyMode = ShortcutEntry.Mode;
+ ImVec2 Size = ImGui::CalcTextSize(KeyModeTitles[CurrentKeyMode]);
+ ImGui::SetCursorPosX(((SubwindowSize.x / 2) - (Size.x / 2)));
+ ImGui::Text(KeyModeTitles[CurrentKeyMode]);
+ ImGui::SetCursorPosX((SubwindowSize.x / 2) - (ImGui::CalcTextSize("-----").x / 2));
+ ImGui::TextColored(ImColor(UI->LayerColors[CurrentKeyMode]), "-----");
+ ImGui::Dummy(ImVec2(1, KeySize/4));
+ while (ShortcutEntry.Key == ImGuiKey_None) {
+ ImVec2 Size = ImGui::CalcTextSize(ShortcutEntry.Name);
+ ImGui::SetCursorPosX(((SubwindowSize.x / 2) - (Size.x / 2)));
+ ImGui::Text(ShortcutEntry.Name);
+ a++;
+ ShortcutEntry = ShortcutArray[a];
+ }
+ ImGui::Dummy(ImVec2(1, KeySize/2));
+ }
+
+ // shortcut text + key
+ Assert(ShortcutEntry.Key != ImGuiKey_None);
+ key_entry KeyEntry = KeyEntries[ShortcutEntry.Key - ImGuiKey_Tab];
+ real32 Padding = KeySize;
+ ImGui::Dummy(ImVec2(Padding, 1));
+ ImGui::SameLine();
+ ImGui::Text(ShortcutEntry.Name);
+ ImGui::SameLine();
+ char buf[64];
+ if (ShortcutEntry.Mods == Mod_None) {
+ sprintf(buf, "%s", KeyEntry.Name);
+ } else if (ShortcutEntry.Mods == Mod_Shift) {
+ sprintf(buf, "%s", KeyEntry.ShiftName);
+ } else {
+ sprintf(buf, "%s + %s", KeyModText[ShortcutEntry.Mods], KeyEntry.Name);
+ }
+ ImVec2 Size = ImGui::CalcTextSize(buf);
+ ImGui::SetCursorPosX(SubwindowSize.x - Size.x - Padding*1.5);
+ ImGui::Text(buf);
+
+ // indicator on keyboard
+ ImVec2 Offset_ScreenPos(0,0);
+ ImVec2 KeyScreenSize(0,0);
+ ImGui_Key_GetUIInfo(KeyEntry, KeySize, &Offset_ScreenPos, &KeyScreenSize);
+ real32 ModeSliverSize = KeySize / key_mode_count;
+ Offset_ScreenPos.x += (ShortcutEntry.Mode * ModeSliverSize);
+ KeyScreenSize = ImVec2(ModeSliverSize, KeyScreenSize.y);
+ ImVec2 MinPos = WindowMinAbs + KeyboardPos + Offset_ScreenPos;
+ draw_list->AddRectFilled(MinPos, MinPos + KeyScreenSize, UI->LayerColors[CurrentKeyMode]);
+ }
+ ImGui::EndChild();
+ if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+
+ State->Split_KeybindUI.Merge(draw_list);
+}
+
+static void
+ImGui_Popups(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io)
+{
+ switch (State->ImGuiPopups)
+ {
+ case popup_none:
+ {
+ } break;
+ case popup_saveas:
+ {
+ ImGui::OpenPopup("Save as");
+ ImGui::SetKeyboardFocusHere();
+ } break;
+ case popup_keybinds:
+ {
+ ImGui::OpenPopup("Keybinds");
+ ImGui::SetKeyboardFocusHere();
+ } break;
+ default:
+ {
+ Assert(0);
+ }
+ }
+ State->ImGuiPopups = popup_none;
+
+ if (ImGui::BeginPopupModal("Save as")) {
+ ImGui::Text("Destination path...");
+ ImGui::InputText("File", State->Filename, 512);
+ if (ImGui::Button("Save file")) {
+ ImGui::Text("Saving...");
+ File_SaveAs(File, State, Memory, State->Filename);
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+ }
+ if (ImGui::BeginPopupModal("Keybinds")) {
+ ImGui_KeybindUI(File, State, UI, Memory, io);
+ }
+}
+
+static void
+ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, sorted_file Sorted)
+{
+ if (ImGui::IsKeyPressed(ImGuiKey_Q)) {
+ State->IsRunning = false;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_W)) {
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ State->Frame_Current = ((State->Frame_Current - 1) < 0) ? 0 : State->Frame_Current - 1;
+ State->UpdateFrame = true;
+ State->UpdateKeyframes = true;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_E)) {
+ if (!io.KeyShift) {
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ State->Frame_Current = ((State->Frame_Current + 1) >= MainComp->Frame_Count) ? 0 : State->Frame_Current + 1;
+ State->UpdateFrame = true;
+ State->UpdateKeyframes = true;
+ } else {
+ State->Brush.EraseMode ^= 1;
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_U)) {
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected) {
+ Layer_ToggleAllChannels(State, Memory, Layer, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray);
+ }
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_X)) {
+ if (State->TimelineMode == timeline_mode_graph && State->Interact_Active == interact_type_keyframe_move) {
+ if (State->Interact_Modifier != 1)
+ State->Interact_Modifier = 1;
+ else
+ State->Interact_Modifier = 0;
+ } else {
+ v4 Temp = UI->Color;
+ UI->Color = UI->AltColor;
+ UI->AltColor = Temp;
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_Y)) {
+ if (State->TimelineMode == timeline_mode_graph && State->Interact_Active == interact_type_keyframe_move) {
+ if (State->Interact_Modifier != 2)
+ State->Interact_Modifier = 2;
+ else
+ State->Interact_Modifier = 0;
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_V)) {
+ State->Tool = tool_default;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_B)) {
+ State->Tool = tool_brush;
+ }
+ // NOTE(fox): File data not tracked on undo tree!
+ if (ImGui::IsKeyPressed(ImGuiKey_N)) {
+ if (io.KeyShift) {
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ if (MainComp->Frame_Start < State->Frame_Current)
+ MainComp->Frame_End = State->Frame_Current;
+ } else {
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ if (MainComp->Frame_End > State->Frame_Current)
+ MainComp->Frame_Start = State->Frame_Current;
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_Tab)) {
+ State->TimelineMode = (State->TimelineMode == timeline_mode_default) ? timeline_mode_graph : timeline_mode_default;
+ UI->GraphZoomSize = ImVec2(0, 0);
+ UI->GraphMoveSize = ImVec2(0, 0);
+ }
+ if (!io.KeyCtrl) {
+ // NOTE(fox): Checking IsWindowHovered seems to be all we need to do to
+ // make per-window hotkeys work; setting it as the focused window causes
+ // problems with popups.
+ if (State->FocusedWindow == focus_timeline) {
+ if (State->TimelineMode == timeline_mode_default) {
+ if (ImGui::IsKeyPressed(ImGuiKey_G)) {
+ Layer_ToggleChannel(File, Memory, 0);
+ Layer_ToggleChannel(File, Memory, 1);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_A)) {
+ Layer_ToggleChannel(File, Memory, 2);
+ Layer_ToggleChannel(File, Memory, 3);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_R)) {
+ Layer_ToggleChannel(File, Memory, 4);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
+ Layer_ToggleChannel(File, Memory, 5);
+ } else if (ImGui::IsKeyPressed(ImGuiKey_T)) {
+ if (io.KeyShift) {
+ Layer_ToggleChannel(File, Memory, 6);
+ } else {
+ Layer_ToggleChannel(File, Memory, 7);
+ }
+ }
+ } else if (State->TimelineMode == timeline_mode_graph) {
+ if (ImGui::IsKeyPressed(ImGuiKey_G)) {
+ State->Interact_Offset[2] = io.MousePos.x;
+ State->Interact_Offset[3] = io.MousePos.y;
+ State->Interact_Active = interact_type_keyframe_move;
+ } else if (ImGui::IsKeyPressed(ImGuiKey_R)) {
+ // State->Interact_Offset[2] = io.MousePos.x;
+ // State->Interact_Offset[3] = io.MousePos.y;
+ // State->Interact_Active = interact_type_keyframe_rotate;
+ } else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
+ State->Interact_Offset[2] = io.MousePos.x;
+ State->Interact_Offset[3] = io.MousePos.y;
+ State->Interact_Active = interact_type_keyframe_scale;
+ }
+ }
+ } else if (State->FocusedWindow == focus_viewport) {
+ if (ImGui::IsKeyPressed(ImGuiKey_T)) {
+ Interact_Transform_Begin(File, Memory, State, io.MousePos, Sorted.CompArray, Sorted.LayerArray);
+ State->Tool = tool_default;
+ }
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
+ if (State->Interact_Active == interact_type_keyframe_move ||
+ State->Interact_Active == interact_type_keyframe_rotate ||
+ State->Interact_Active == interact_type_keyframe_scale) {
+ State->Interact_Offset[0] = 0;
+ State->Interact_Offset[1] = 0;
+ State->Interact_Offset[2] = 0;
+ State->Interact_Offset[3] = 0;
+ State->Interact_Active = interact_type_none;
+ State->Interact_Modifier = 0;
+ State->UpdateFrame = true;
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_Space) ) {
+ if (io.KeyShift) {
+ State->RerouteEffects = true;
+ } else {
+ State->IsPlaying ^= 1;
+ State->HotFramePerf = 1;
+ State->PlayAudio = false;
+ State->FramePlayingCount = 0;
+ switch (SDL_GetAudioStatus())
+ {
+ case SDL_AUDIO_STOPPED: Assert(0); break;
+ case SDL_AUDIO_PLAYING: SDL_PauseAudio(1); break;
+ case SDL_AUDIO_PAUSED: SDL_PauseAudio(0); break;
+ default: Assert(0); break;
+ }
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_2)) {
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected && Layer->IsPrecomp) {
+ Layer->Precomp_Toggled ^= 1;
+ }
+ }
+ }
+
+ if (ImGui::IsKeyPressed(ImGuiKey_Delete))
+ {
+ State->HotkeyInput = hotkey_deletelayer;
+ }
+
+ if (io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_Slash))
+ {
+ State->ImGuiPopups = popup_keybinds;
+ }
+
+#if DEBUG
+ if (ImGui::IsKeyPressed(ImGuiKey_3))
+ {
+ // State->ImGuiPopups = popup_keybinds;
+ Debug.DisableAlpha = 0;
+ State->UpdateFrame = true;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_4))
+ {
+ Debug.DisableAlpha = 1;
+ State->UpdateFrame = true;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_5))
+ {
+ Debug.DisableAlpha = 2;
+ State->UpdateFrame = true;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_F))
+ {
+ sprintf(State->DummyName, "test2");
+ File_Open(File, State, Memory, State->DummyName);
+ State->UpdateFrame = true;
+ State->MostRecentlySelectedLayer = 0;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_0))
+ {
+ Debug.ReloadUI ^= 1;
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_1))
+ {
+ Debug.ToggleWindow ^= 1;
+ }
+#endif
+
+ bool32 mod_key = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
+ if (mod_key) {
+ if (ImGui::IsKeyPressed(ImGuiKey_S)) {
+ if (io.KeyShift) {
+ State->ImGuiPopups = popup_saveas;
+ } else {
+ if (State->Filename[0] == '\0') {
+ State->ImGuiPopups = popup_saveas;
+ } else {
+ File_SaveAs(File, State, Memory, State->Filename);
+ }
+ }
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_C)) {
+ Clipboard_Store(File, State, Memory, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray);
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_V)) {
+ Clipboard_Paste(File, State, Memory, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyArray);
+ }
+ if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
+ if (io.KeyShift) {
+ State->HotkeyInput = hotkey_redo;
+ } else {
+ State->HotkeyInput = hotkey_undo;
+ }
+ }
+ }
+}
+
+static void
+ImGui_Menu(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io)
+{
+ bool open = true;
+ ImGui::Begin("Menu", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar);
+ if (ImGui::BeginMenuBar())
+ {
+ if (ImGui::BeginMenu("File"))
+ {
+ if (ImGui::MenuItem("Save", "Ctrl+S"))
+ {
+ if (State->Filename[0] == '\0')
+ State->ImGuiPopups = popup_saveas;
+ else
+ File_SaveAs(File, State, Memory, State->Filename);
+ }
+ if (ImGui::MenuItem("Save as", "Ctrl+Shift+S"))
+ {
+ State->ImGuiPopups = popup_saveas;
+ }
+ if (ImGui::BeginMenu("Open file"))
+ {
+ ImGui::InputText("Filename", State->DummyName, 512);
+ if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
+ if (File_Open(File, State, Memory, State->DummyName)) {
+ State->UpdateFrame = true;
+ } else {
+ PostMsg(State, "File not found.");
+ }
+ }
+ ImGui::EndMenu();
+ }
+ ImGui::EndMenu();
+ }
+ if (ImGui::BeginMenu("Layer"))
+ {
+ if (ImGui::BeginMenu("Import source from file"))
+ {
+ ImGui::InputText("Path to image", State->DummyName2, 512);
+ if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
+ int SourceIndex = Source_Generate(File, State, Memory, (void *)State->DummyName2);
+ State->UpdateFrame = true;
+ }
+ ImGui::EndMenu();
+ }
+ ImGui::EndMenu();
+ }
+ if (ImGui::BeginMenu("Window"))
+ {
+#if STABLE
+ if (ImGui::Selectable("Stable Diffusion tools", UI->StableEnabled))
+ UI->StableEnabled ^= 1;
+ ImGui::EndMenu();
+#endif
+ }
+ ImGui::EndMenuBar();
+ }
+
+ ImGui::End();
+}
+
+static void
+ImGui_EffectsPanel(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io)
+{
+ ImGui::Begin("Effects list", NULL);
+ if (State->RerouteEffects) {
+ ImGui::SetKeyboardFocusHere();
+ State->RerouteEffects = 0;
+ }
+ int value_changed = ImGui::InputText("Effect name...", State->filter.InputBuf, IM_ARRAYSIZE(State->filter.InputBuf),
+ ImGuiInputTextFlags_CallbackCompletion, EffectConsoleCallback);
+
+ if (Hacko) {
+ if (!io.KeyShift)
+ EffectSel++;
+ else
+ EffectSel--;
+ Hacko = 0;
+ }
+ if (value_changed) {
+ State->filter.Build();
+ EffectSel = -1;
+ }
+ // Enter conveniently deactivates the InputText field
+ if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
+ int32 p = 0;
+ for (int32 i = 0; i < State->Playhead_Effect; i++) {
+ header_effect *EffectHeader = &State->Effect[i];
+ if (State->filter.PassFilter(EffectHeader->Name)) {
+ if (EffectSel == p && State->MostRecentlySelectedLayer != -1) {
+ Effect_Add(File, State, Memory, i);
+ State->UpdateFrame = true;
+ }
+ p++;
+ }
+ }
+ EffectSel = -1;
+ }
+ int32 p = 0;
+ for (int32 i = 0; i < State->Playhead_Effect; i++) {
+ header_effect *EffectHeader = &State->Effect[i];
+ if (State->filter.PassFilter(EffectHeader->Name)) {
+ bool t = false;
+ if (EffectSel == p) {
+ t = true;
+ }
+ ImGui::Selectable(EffectHeader->Name, &t);
+ if (ImGui::IsItemClicked()) {
+ if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && State->MostRecentlySelectedLayer != -1) {
+ Effect_Add(File, State, Memory, i);
+ State->UpdateFrame = true;
+ }
+ }
+ p++;
+ }
+ }
+ ImGui::End();
+}
+
+static char ImGuiPrefs[] = "[Window][DockSpaceViewport_11111111]\n"
+"Pos=0,0\n"
+"Size=3840,2160\n"
+"Collapsed=0\n"
+"\n"
+"[Window][Debug##Default]\n"
+"Pos=122,442\n"
+"Size=400,400\n"
+"Collapsed=0\n"
+"\n"
+"[Window][Viewport]\n"
+"Pos=443,34\n"
+"Size=2872,1565\n"
+"Collapsed=0\n"
+"DockId=0x00000010,0\n"
+"\n"
+"[Window][###Properties]\n"
+"Pos=0,34\n"
+"Size=441,1565\n"
+"Collapsed=0\n"
+"DockId=0x0000000B,0\n"
+"\n"
+"[Window][Timeline]\n"
+"Pos=0,1601\n"
+"Size=3840,559\n"
+"Collapsed=0\n"
+"DockId=0x0000000A,0\n"
+"\n"
+"[Window][Dear ImGui Demo]\n"
+"Pos=2677,34\n"
+"Size=523,437\n"
+"Collapsed=0\n"
+"DockId=0x00000011,1\n"
+"\n"
+"[Window][Files]\n"
+"Pos=3317,604\n"
+"Size=523,743\n"
+"Collapsed=0\n"
+"DockId=0x00000007,0\n"
+"\n"
+"[Window][Effects list]\n"
+"Pos=3317,1349\n"
+"Size=523,250\n"
+"Collapsed=0\n"
+"DockId=0x00000008,0\n"
+"\n"
+"[Window][Graph editor]\n"
+"Pos=0,949\n"
+"Size=3200,526\n"
+"Collapsed=0\n"
+"DockId=0x00000009,0\n"
+"\n"
+"[Window][undotree]\n"
+"Pos=2677,473\n"
+"Size=523,572\n"
+"Collapsed=0\n"
+"DockId=0x00000007,2\n"
+"\n"
+"[Window][memoryviewer]\n"
+"Pos=50,273\n"
+"Size=800,200\n"
+"Collapsed=0\n"
+"\n"
+"[Window][Example: Custom rendering]\n"
+"Pos=758,789\n"
+"Size=485,414\n"
+"Collapsed=0\n"
+"\n"
+"[Window][Memory viewer]\n"
+"Pos=2677,473\n"
+"Size=523,572\n"
+"Collapsed=0\n"
+"DockId=0x00000007,1\n"
+"\n"
+"[Window][Graph info]\n"
+"Pos=2838,1265\n"
+"Size=235,353\n"
+"Collapsed=0\n"
+"\n"
+"[Window][Properties]\n"
+"Pos=0,34\n"
+"Size=495,1056\n"
+"Collapsed=0\n"
+"DockId=0x0000000F,0\n"
+"\n"
+"[Window][Colors]\n"
+"Pos=3317,34\n"
+"Size=523,568\n"
+"Collapsed=0\n"
+"DockId=0x00000011,0\n"
+"\n"
+"[Window][Menu]\n"
+"Pos=0,0\n"
+"Size=3840,32\n"
+"Collapsed=0\n"
+"DockId=0x0000000D,0\n"
+"\n"
+"[Window][Stable Diffusion]\n"
+"Pos=2206,684\n"
+"Size=421,462\n"
+"Collapsed=0\n"
+"\n"
+"[Window][SD prompt input]\n"
+"Pos=2677,473\n"
+"Size=523,541\n"
+"Collapsed=0\n"
+"DockId=0x00000007,2\n"
+"\n"
+"[Window][Example: Console]\n"
+"Pos=747,851\n"
+"Size=520,600\n"
+"Collapsed=0\n"
+"\n"
+"[Window][SD gallery]\n"
+"Pos=0,718\n"
+"Size=441,557\n"
+"Collapsed=0\n"
+"DockId=0x0000000C,0\n"
+"\n"
+"[Window][Save as]\n"
+"Pos=1782,1058\n"
+"Size=300,116\n"
+"Collapsed=0\n"
+"\n"
+"[Window][Keybinds]\n"
+"Pos=1573,769\n"
+"Size=750,639\n"
+"Collapsed=0\n"
+"\n"
+"[Table][0x861D378E,3]\n"
+"Column 0 Weight=1.0000\n"
+"Column 1 Weight=1.0000\n"
+"Column 2 Weight=1.0000\n"
+"\n"
+"[Table][0x1F146634,3]\n"
+"RefScale=13\n"
+"Column 0 Width=63\n"
+"Column 1 Width=63\n"
+"Column 2 Width=63\n"
+"\n"
+"[Table][0x64418101,3]\n"
+"RefScale=13\n"
+"Column 0 Width=63\n"
+"Column 1 Width=63\n"
+"Column 2 Width=63\n"
+"\n"
+"[Table][0xC9935533,3]\n"
+"Column 0 Weight=1.0000\n"
+"Column 1 Weight=1.0000\n"
+"Column 2 Weight=1.0000\n"
+"\n"
+"[Docking][Data]\n"
+"DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,0 Size=3840,2160 Split=Y Selected=0x13926F0B\n"
+" DockNode ID=0x0000000D Parent=0x8B93E3BD SizeRef=3200,32 HiddenTabBar=1 Selected=0xA57AB2C6\n"
+" DockNode ID=0x0000000E Parent=0x8B93E3BD SizeRef=3200,1299 Split=Y\n"
+" DockNode ID=0x00000001 Parent=0x0000000E SizeRef=3200,1205 Split=X Selected=0x13926F0B\n"
+" DockNode ID=0x00000003 Parent=0x00000001 SizeRef=441,1171 Split=Y Selected=0xDBB8CEFA\n"
+" DockNode ID=0x0000000B Parent=0x00000003 SizeRef=521,425 Selected=0xDBB8CEFA\n"
+" DockNode ID=0x0000000C Parent=0x00000003 SizeRef=521,347 Selected=0x56290987\n"
+" DockNode ID=0x00000004 Parent=0x00000001 SizeRef=1690,1171 Split=X Selected=0x13926F0B\n"
+" DockNode ID=0x00000005 Parent=0x00000004 SizeRef=1165,1171 Split=X Selected=0x13926F0B\n"
+" DockNode ID=0x0000000F Parent=0x00000005 SizeRef=495,856 Selected=0x199AB496\n"
+" DockNode ID=0x00000010 Parent=0x00000005 SizeRef=2199,856 CentralNode=1 Selected=0x13926F0B\n"
+" DockNode ID=0x00000006 Parent=0x00000004 SizeRef=523,1171 Split=Y Selected=0x86FA2F90\n"
+" DockNode ID=0x00000011 Parent=0x00000006 SizeRef=483,437 Selected=0xBF7DFDC9\n"
+" DockNode ID=0x00000012 Parent=0x00000006 SizeRef=483,766 Split=Y Selected=0x59A2A092\n"
+" DockNode ID=0x00000007 Parent=0x00000012 SizeRef=523,572 Selected=0x86FA2F90\n"
+" DockNode ID=0x00000008 Parent=0x00000012 SizeRef=523,192 Selected=0x812F222D\n"
+" DockNode ID=0x00000002 Parent=0x0000000E SizeRef=3200,559 Split=Y Selected=0x0F18B61B\n"
+" DockNode ID=0x00000009 Parent=0x00000002 SizeRef=3250,526 Selected=0xA1F22F4D\n"
+" DockNode ID=0x0000000A Parent=0x00000002 SizeRef=3250,323 HiddenTabBar=1 Selected=0x0F18B61B\n"
+"\0";