Skip to content

Commit 6b718e9

Browse files
committed
gui backend hardening
1 parent 395cb27 commit 6b718e9

File tree

15 files changed

+185
-51
lines changed

15 files changed

+185
-51
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Bring your own GUI
2+
3+
Clapeze doesn't come with any gui framework by default. Instead, you can subclass `clapeze::BaseGuiFeature`. This is a thin wrapper around the clap `EXT_GUI` extension.
4+
5+
```c++
6+
#include "clapeze/ext/gui.h"
7+
8+
class MyGuiFeature : public clapeze::BaseGuiFeature {
9+
bool IsApiSupported(ClapWindowApi api, bool isFloating) override;
10+
bool GetPreferredApi(ClapWindowApi& apiOut, bool& isFloatingOut) override;
11+
bool Create(ClapWindowApi api, bool isFloating) override;
12+
void Destroy() override;
13+
bool SetScale(double scale) override;
14+
bool GetSize(uint32_t& widthOut, uint32_t& heightOut) override;
15+
bool CanResize() override;
16+
bool GetResizeHints(clap_gui_resize_hints_t& hintsOut) override;
17+
bool AdjustSize(uint32_t& widthInOut, uint32_t& heightInOut) override;
18+
bool SetSize(uint32_t width, uint32_t height) override;
19+
bool SetParent(WindowHandle handle) override;
20+
bool SetTransient(WindowHandle handle) override;
21+
void SuggestTitle(std::string_view title) override;
22+
bool Show() override;
23+
bool Hide() override;
24+
}
25+
```
26+
27+
And then in your plugin:
28+
29+
```c++
30+
void MyPlugin::Config() {
31+
...
32+
ConfigFeature<MyGuiFeature>(GetHost());
33+
...
34+
}
35+
```
36+
37+
# How to implement
38+
39+
the clap gui extension is not especially easy to implement, so let's discuss the requirements quickly.
40+
41+
---
42+
43+
### M Processes, N Windows
44+
45+
The user might open up multiple instances of your plugin at the same time, so you should be robust to that. They may be sharing a "main" thread, so blocking event loops are not an option. Some hosts will seperate plugins (including those by third parties) by process, others won't, so if your framework has process-shared state be careful not to pollute it.
46+
47+
### The Windowing Framework is the interface
48+
49+
Clap doesn't define an event loop or surface system, instead, you get a `clap_window_t`, which is a native handle to the parent window. You're expected to figure out your own event loop. You're also expected to figure out highdpi support (though the host _might_ provide a desired zoom level).
50+
51+
### Embed vs Floating
52+
53+
Almost every host and windowing framework expects to "embed" a window into the parent handle it provides you. this means that the plugin host actually creates the stuff you'd normally associate with a window, the decorations around it, and often little widgets on the inside for managing it.
54+
55+
Linux/Wayland doesn't have any kind of equivalent to the concept of embedding, so the API's surface area doubles to support "floating" windows. These are fully separate windows with a looser connection to the parent window; this is what happen when you spawn a dialog or file picker, for example. This is currently supported in very few hosts, so I might hide it from Clapeze's API. To be continued!
56+
57+
### Create/Destroy are common
58+
59+
Many hosts will create windows when the user focuses them, and then destroy those windows when the user changes focus in the parent app. this means you'll be calling create and delete multiple times in a given session, and it makes sense to optimize these as much as possible. In my own plugins I will create gui resources on the first call to create, but only delete them when the entire plugin is deleted; this is to avoid expensive recreate calls in this loop.
60+
61+
---
62+
63+
With these requirements added up, you may find it easier to write separate, per-platform window backends that ensure these rules are met, as opposed to using a cross-platform windowing api like SDL.

daw/assets/kitskeys.glb

1.09 MB
Binary file not shown.

daw/assets/kitskeys.packed.glb

-348 KB
Binary file not shown.

daw/pack-meshes.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
find assets -maxdepth 1 -name '*.glb' ! -name '*.packed.glb' -print0 |
44
while IFS= read -r -d '' file; do
5-
gltfpack -v -cc -tc -mi -i "$file" -o "${file%.glb}.packed.glb"
5+
#gltfpack -v -cc -tc -mi -i "$file" -o "${file%.glb}.packed.glb"
6+
gltfpack -v -i "$file" -o "${file%.glb}.packed.glb"
67
done

kitgui/cmake/imgui.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ add_library(imgui STATIC EXCLUDE_FROM_ALL
66
${IMGUI_DIR}/imgui_draw.cpp
77
${IMGUI_DIR}/imgui_tables.cpp
88
${IMGUI_DIR}/imgui_widgets.cpp
9+
${IMGUI_DIR}/misc/cpp/imgui_stdlib.cpp
910
)
11+
target_include_directories(imgui PRIVATE ${IMGUI_DIR})
1012
target_include_directories(imgui INTERFACE ${IMGUI_DIR})
1113

1214
add_library(imgui_opengl STATIC EXCLUDE_FROM_ALL

kitgui/examples/3dscene.cpp

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,71 @@
11
#include <Magnum/Math/Color.h>
22
#include <imgui.h>
3+
#include <misc/cpp/imgui_stdlib.h>
4+
#include <memory>
35
#include "kitgui/app.h"
46
#include "kitgui/context.h"
57
#include "kitgui/gfx/scene.h"
68
#include "kitgui/kitgui.h"
9+
#include "kitgui/types.h"
710

811
class MyApp : public kitgui::BaseApp {
912
public:
10-
explicit MyApp(kitgui::Context& mContext) : kitgui::BaseApp(mContext), mScene(mContext) {
11-
mContext.SetSizeConfig({400, 400});
12-
mContext.SetClearColor(Magnum::Math::Color4(1.0f, 1.0f, 1.0f, 1.0f));
13-
}
13+
explicit MyApp(kitgui::Context& mContext)
14+
: kitgui::BaseApp(mContext), mScene(std::make_unique<kitgui::Scene>(mContext)) {}
1415
~MyApp() = default;
1516

1617
protected:
1718
void OnActivate() override {
18-
mScene.Load(PROJECT_DIR "/assets/duck.glb");
19-
// mScene.PlayAnimationByName("Suzanne");
19+
mScene->Load(mFilePath);
20+
GetContext().SetClearColor(mClearColor);
2021
}
2122
void OnUpdate() override {
22-
mScene.Update();
23-
ImGui::Text("Oh yeah, gamer time!");
23+
mScene->Update();
24+
if (mShowUi) {
25+
ImGui::SetNextWindowPos({ImGui::GetWindowWidth() - 300, ImGui::GetWindowHeight() - 200}, ImGuiCond_Once);
26+
ImGui::SetNextWindowSize({300, 200}, ImGuiCond_Once);
27+
if (ImGui::Begin("Viewer", &mShowUi)) {
28+
ImGui::InputText("file", &mFilePath);
29+
if (ImGui::IsItemDeactivatedAfterEdit()) {
30+
mScene.reset();
31+
mScene = std::make_unique<kitgui::Scene>(GetContext());
32+
mScene->Load(mFilePath);
33+
}
34+
35+
uint32_t w{};
36+
uint32_t h{};
37+
GetContext().GetSize(w, h);
38+
int32_t data[2] = {static_cast<int32_t>(w), static_cast<int32_t>(h)};
39+
ImGui::InputInt2("size", data);
40+
if (ImGui::IsItemDeactivatedAfterEdit()) {
41+
w = static_cast<uint32_t>(data[0]);
42+
h = static_cast<uint32_t>(data[1]);
43+
GetContext().SetSizeConfig({.startingWidth = w, .startingHeight = h});
44+
GetContext().SetSizeDirectly(w, h, false);
45+
}
46+
47+
if (ImGui::ColorEdit4("bgcolor", reinterpret_cast<float*>(&mClearColor))) {
48+
GetContext().SetClearColor(mClearColor);
49+
}
50+
}
51+
ImGui::End();
52+
}
2453
}
25-
void OnDraw() override { mScene.Draw(); }
54+
void OnDraw() override { mScene->Draw(); }
2655

2756
private:
28-
kitgui::Scene mScene;
57+
std::unique_ptr<kitgui::Scene> mScene;
58+
bool mShowUi = true;
59+
std::string mFilePath{PROJECT_DIR "/assets/duck.glb"};
60+
kitgui::Color4 mClearColor{0.1f, 0.4f, 0.4f, 1.0f};
2961
};
3062

3163
int main() {
3264
kitgui::Context::init(kitgui::WindowApi::Any, "kitgui");
3365

3466
{
3567
kitgui::Context ctx1([](kitgui::Context& ctx) { return std::make_unique<MyApp>(ctx); });
68+
ctx1.SetSizeConfig({400, 400, true, false});
3669
ctx1.Create(true);
3770
ctx1.Show();
3871

kitgui/examples/CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ add_compile_definitions(-DPROJECT_DIR="${CMAKE_SOURCE_DIR}")
1515
#
1616
# and it'll be available to link, as below.
1717

18-
add_executable(demo demo.cpp)
19-
target_link_libraries(demo kitgui)
18+
add_executable(imguiDemo imguiDemo.cpp)
19+
target_link_libraries(imguiDemo kitgui)
2020

2121
add_executable(multiwindow multiwindow.cpp)
2222
target_link_libraries(multiwindow kitgui)
2323

2424
add_executable(3dscene 3dscene.cpp)
2525
target_link_libraries(3dscene kitgui)
2626

27-
add_executable(immediatemode immediatemode.cpp)
28-
target_link_libraries(immediatemode kitgui)
27+
add_executable(customControls customControls.cpp)
28+
target_link_libraries(customControls kitgui)

kitgui/examples/multiwindow.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,32 @@ using namespace Magnum;
77

88
class MyApp2 : public kitgui::BaseApp {
99
public:
10-
explicit MyApp2(kitgui::Context& mContext) : kitgui::BaseApp(mContext) {}
10+
explicit MyApp2(kitgui::Context& mContext, int32_t idx) : kitgui::BaseApp(mContext), mIdx(idx) {}
1111
~MyApp2() = default;
1212

1313
protected:
1414
void OnActivate() override {}
15-
void OnUpdate() override { ImGui::Text("Window.... 2!!!"); }
15+
void OnUpdate() override {
16+
ImGui::Text("Window: %d", mIdx);
17+
ImGui::Text("Scale: %f", GetContext().GetUIScale());
18+
}
1619

1720
void OnDraw() override {}
1821

1922
private:
23+
int32_t mIdx;
2024
};
2125

2226
int main() {
2327
kitgui::Context::init(kitgui::WindowApi::Any, "kitgui");
2428

2529
{
26-
kitgui::Context ctx1([](kitgui::Context& ctx) { return std::make_unique<MyApp2>(ctx); });
30+
kitgui::Context ctx1([](kitgui::Context& ctx) { return std::make_unique<MyApp2>(ctx, 1); });
31+
ctx1.SetClearColor({0.4f, 0.1f, 0.4f, 1.0f});
2732
ctx1.Create(true);
2833

29-
kitgui::Context ctx2([](kitgui::Context& ctx) { return std::make_unique<MyApp2>(ctx); });
34+
kitgui::Context ctx2([](kitgui::Context& ctx) { return std::make_unique<MyApp2>(ctx, 2); });
35+
ctx2.SetClearColor({0.1f, 0.4f, 0.4f, 1.0f});
3036
ctx2.Create(true);
3137

3238
ctx1.Show();

0 commit comments

Comments
 (0)