#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; Memory->PurgeCache = 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->UncommitedKeyframe = 1; 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 (State->UncommitedKeyframe) { Memory->PurgeCache = true; State->UncommitedKeyframe = false; } } 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; if (State->UncommitedKeyframe) { Memory->PurgeCache = true; State->UncommitedKeyframe = false; } } 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; State->UpdateKeyframes = true; Memory->PurgeCache = 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; if (State->UncommitedKeyframe) { Memory->PurgeCache = true; State->UncommitedKeyframe = false; } 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";