diff options
Diffstat (limited to 'src/imgui_ui.cpp')
-rw-r--r-- | src/imgui_ui.cpp | 1502 |
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"; |