xref: /aosp_15_r20/external/skia/tools/viewer/ImGuiLayer.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7 
8 #ifndef ImGuiLayer_DEFINED
9 #define ImGuiLayer_DEFINED
10 
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/base/SkTArray.h"
17 #include "include/private/base/SkTPin.h"
18 #include "include/private/base/SkTemplates.h"
19 #include "tools/sk_app/Window.h"
20 
21 #include <algorithm>
22 #include <functional>
23 
24 #include "imgui.h"
25 
26 class SkCanvas;
27 class SkSurface;
28 
29 namespace skui {
30 enum class InputState;
31 enum class Key;
32 enum class ModifierKey;
33 }  // namespace skui
34 
35 namespace ImGui {
36 
37 // Helper object for drawing in a widget region, with draggable points
38 struct DragCanvas {
39     DragCanvas(const void* id, SkPoint tl = { 0.0f, 0.0f }, SkPoint br = { 1.0f, 1.0f },
40                float aspect = -1.0f)
41             : fID(0), fDragging(false) {
42         ImGui::PushID(id);
43         fDrawList = ImGui::GetWindowDrawList();
44 
45         // Logical size
46         SkScalar w = SkTAbs(br.fX - tl.fX),
47                  h = SkTAbs(br.fY - tl.fY);
48 
49         // Determine aspect ratio automatically by default
50         if (aspect < 0) {
51             aspect = h / w;
52         }
53 
54         float availWidth = std::max(ImGui::GetContentRegionAvailWidth(), 1.0f);
55         fPos = ImGui::GetCursorScreenPos();
56         fSize = ImVec2(availWidth, availWidth * aspect);
57 
58         SkPoint local[4] = {
59             { tl.fX, tl.fY },
60             { br.fX, tl.fY },
61             { tl.fX, br.fY },
62             { br.fX, br.fY },
63         };
64         SkPoint screen[4] = {
65             { fPos.x          , fPos.y           },
66             { fPos.x + fSize.x, fPos.y           },
67             { fPos.x          , fPos.y + fSize.y },
68             { fPos.x + fSize.x, fPos.y + fSize.y },
69         };
70         fLocalToScreen.setPolyToPoly(local, screen, 4);
71         fScreenToLocal.setPolyToPoly(screen, local, 4);
72     }
73 
~DragCanvasDragCanvas74     ~DragCanvas() {
75         ImGui::SetCursorScreenPos(ImVec2(fPos.x, fPos.y + fSize.y));
76         ImGui::Spacing();
77         ImGui::PopID();
78     }
79 
fillColorDragCanvas80     void fillColor(ImU32 color) {
81         fDrawList->AddRectFilled(fPos, ImVec2(fPos.x + fSize.x, fPos.y + fSize.y), color);
82     }
83 
84     void dragPoint(SkPoint* p, bool tooltip = false, ImU32 color = 0xFFFFFFFF) {
85         // Transform points from logical coordinates to screen coordinates
86         SkPoint center = fLocalToScreen.mapXY(p->fX, p->fY);
87 
88         // Invisible 10x10 button
89         ImGui::PushID(fID++);
90         ImGui::SetCursorScreenPos(ImVec2(center.fX - 5, center.fY - 5));
91         ImGui::InvisibleButton("", ImVec2(10, 10));
92 
93         if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) {
94             // Update screen position to track mouse, clamped to our area
95             ImGuiIO& io = ImGui::GetIO();
96             center.set(SkTPin(io.MousePos.x, fPos.x, fPos.x + fSize.x),
97                        SkTPin(io.MousePos.y, fPos.y, fPos.y + fSize.y));
98 
99             // Update local coordinates for the caller
100             *p = fScreenToLocal.mapXY(center.fX, center.fY);
101             fDragging = true;
102         }
103 
104         if (tooltip && ImGui::IsItemHovered()) {
105             ImGui::SetTooltip("x: %.3f\ny: %.3f", p->fX, p->fY);
106         }
107 
108         ImGui::PopID();
109 
110         fScreenPoints.push_back(ImVec2(center.fX, center.fY));
111         fDrawList->AddCircle(fScreenPoints.back(), 5.0f, color);
112     }
113 
114     ImDrawList* fDrawList;
115 
116     // Location and dimensions (in screen coordinates)
117     ImVec2 fPos;
118     ImVec2 fSize;
119 
120     // Screen coordinates of points (for additional user drawing)
121     skia_private::STArray<4, ImVec2, true> fScreenPoints;
122 
123     // To simplify dragPoint
124     SkMatrix fLocalToScreen;
125     SkMatrix fScreenToLocal;
126 
127     int fID;
128     bool fDragging;
129 };
130 
131 }  // namespace ImGui
132 
133 class ImGuiLayer : public sk_app::Window::Layer {
134 public:
135     ImGuiLayer();
136     ~ImGuiLayer() override;
137 
138     void setScaleFactor(float scaleFactor);
139 
140     typedef std::function<void(SkCanvas*)> SkiaWidgetFunc;
141     void skiaWidget(const ImVec2& size, SkiaWidgetFunc func);
142 
143     void onAttach(sk_app::Window* window) override;
144     void onPrePaint() override;
145     void onPaint(SkSurface*) override;
146     bool onMouse(int x, int y, skui::InputState state, skui::ModifierKey modifiers) override;
147     bool onMouseWheel(float delta, int x, int y, skui::ModifierKey modifiers) override;
148     bool onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers) override;
149     bool onChar(SkUnichar c, skui::ModifierKey modifiers) override;
150 
151 private:
152     sk_app::Window* fWindow;
153     SkPaint fFontPaint;
154     skia_private::TArray<SkiaWidgetFunc> fSkiaWidgetFuncs;
155 };
156 
157 #endif
158