diff --git a/Project.toml b/Project.toml index 038eff0..61da745 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "CImGui" uuid = "5d785b6c-b76f-510e-a07c-3070796c7e87" authors = ["Yupei Qi "] -version = "2.0.0" +version = "2.1.0" [deps] CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" @@ -10,15 +10,18 @@ CSyntax = "ea656a56-6ca6-5dda-bba5-7b6963a5f74c" [weakdeps] GLFW = "f7f18e0c-5ee9-5ccd-a5bf-e8befd85ed98" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" ModernGL = "66fc600b-dfda-50eb-8b99-91cfa97b1301" [extensions] GlfwOpenGLBackend = ["GLFW", "ModernGL"] +MakieIntegration = ["GLFW", "ModernGL", "GLMakie"] [compat] CEnum = "0.4, 0.5" CImGuiPack_jll = "0.3.0" CSyntax = "0.4" GLFW = "3" +GLMakie = "0.10.5" ModernGL = "1" julia = "1.9" diff --git a/docs/make.jl b/docs/make.jl index 1d76e80..c6043dc 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -26,7 +26,7 @@ makedocs(; size_threshold=500000, size_threshold_warn=400000 ), - pages=["index.md", "api.md", "backends.md", "changelog.md"] + pages=["index.md", "api.md", "backends.md", "makie.md", "changelog.md"] ) deploydocs(; diff --git a/docs/src/_changelog.md b/docs/src/_changelog.md index 9d7fb86..1268ff5 100644 --- a/docs/src/_changelog.md +++ b/docs/src/_changelog.md @@ -6,10 +6,11 @@ CurrentModule = CImGui This documents notable changes in CImGui.jl. The format is based on [Keep a Changelog](https://keepachangelog.com). -## Unreleased +## [v2.1.0] - 2024-07-29 ### Added - The OpenGL version can now be set with [`render()`](@ref). +- Experimental [Makie integration](@ref) ([#133]). ## [v2.0.0] - 2024-06-27 diff --git a/docs/src/api.md b/docs/src/api.md index eedfdb7..3c9fc72 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -17,5 +17,5 @@ imgui_version ```@autodocs Modules = [CImGui] Order = [:constant, :function, :type] -Filter = t -> nameof(t) ∉ (:imgui_version, :render, :set_backend) +Filter = t -> nameof(t) ∉ (:imgui_version, :render, :set_backend, :MakieFigure) ``` diff --git a/docs/src/makie.md b/docs/src/makie.md new file mode 100644 index 0000000..4dda734 --- /dev/null +++ b/docs/src/makie.md @@ -0,0 +1,18 @@ +```@meta +CurrentModule = CImGui +``` + +# Makie integration + +We currently have *very experimental* [Makie](https://docs.makie.org/stable) +support through +[GLMakie](https://docs.makie.org/stable/explanations/backends/glmakie). GLMakie +mostly works around a `Screen{T}` object to display a scene, where `T` is some +OpenGL-supporting window. GLMakie sets this to a `GLFW.Window`, but we've made a +custom window type to represent a single `Figure` to be drawn in ImGui. What we +get from GLMakie is a framebuffer with a color image texture attachment, and +that's displayed by us as an image. + +```@docs +MakieFigure +``` diff --git a/examples/makie_demo.jl b/examples/makie_demo.jl new file mode 100644 index 0000000..5d51682 --- /dev/null +++ b/examples/makie_demo.jl @@ -0,0 +1,86 @@ +import GLFW +using GLMakie +import GLMakie.Makie as Makie +import CImGui as ig +import CImGui.CSyntax: @c +import ModernGL as gl + + +ig.set_backend(:GlfwOpenGL3) + +function generate_data(type::Symbol=:random, N=1000) + if type === :random + [Point2f(i, rand()) for i in 1:N] + end +end + +function HelpMarker(msg) + ig.TextDisabled("(?)"); + + if ig.IsItemHovered() && ig.BeginTooltip() + ig.PushTextWrapPos(ig.GetFontSize() * 35.0); + ig.TextUnformatted(msg); + ig.PopTextWrapPos(); + ig.EndTooltip(); + end +end + +function makie_demo(; engine=nothing) + # Create a plot + f = Figure() + scene = Makie.get_scene(f) + ax1 = Axis(f[1, 1]; title="Random data") + data = Observable(generate_data()) + lines!(ax1, data) + data2 = Observable(generate_data()) + ax2 = Axis(f[2, 1]) + lines!(ax2, data2) + + ctx = ig.CreateContext() + io = ig.GetIO() + io.ConfigFlags = unsafe_load(io.ConfigFlags) | ig.lib.ImGuiConfigFlags_DockingEnable + io.ConfigFlags = unsafe_load(io.ConfigFlags) | ig.lib.ImGuiConfigFlags_ViewportsEnable + + ax1_tight_spacing = true + auto_resize_x = true + auto_resize_y = false + + # Start the GUI + ig.render(ctx; engine, window_size=(1280, 760), window_title="ImGui Window") do + ig.Begin("Makie demo") + + if ig.Button("Random data") + data[] = generate_data() + end + + @c ig.Checkbox("Ax1 tight tick spacing", &ax1_tight_spacing) + ig.SameLine() + HelpMarker(""" + Try zooming into the top plot, if this option is disabled + the axis will not resize itself to stop clipping the tick labels on the Y axis. + """) + + @c ig.Checkbox("Auto resize X", &auto_resize_x) + ig.SameLine() + @c ig.Checkbox("Auto resize Y", &auto_resize_y) + + if ig.MakieFigure("plot", f; auto_resize_x, auto_resize_y) + if ax1_tight_spacing + Makie.tight_ticklabel_spacing!(ax1) + end + + Makie.tight_ticklabel_spacing!(ax2) + end + + ig.Text("Mouse position in scene: $(scene.events.mouseposition[])") + ig.Text("Scene size: $(size(scene))") + ig.Text("Mouse position in ax1: $(mouseposition(ax1))") + + ig.End() + end +end + +# Run automatically if the script is launched from the command-line +if !isempty(Base.PROGRAM_FILE) + makie_demo() +end diff --git a/ext/GlfwOpenGLBackend.jl b/ext/GlfwOpenGLBackend.jl index 4016606..70011e9 100644 --- a/ext/GlfwOpenGLBackend.jl +++ b/ext/GlfwOpenGLBackend.jl @@ -1,7 +1,7 @@ module GlfwOpenGLBackend import CSyntax: @c -import CImGui +import CImGui as ig import CImGui.lib as lib import GLFW import ModernGL as GL @@ -24,7 +24,7 @@ end const g_ImageTexture = Dict{Int, GL.GLuint}() -function CImGui._create_image_texture(::Val{:GlfwOpenGL3}, image_width, image_height; format=GL.GL_RGBA, type=GL.GL_UNSIGNED_BYTE) +function ig._create_image_texture(::Val{:GlfwOpenGL3}, image_width, image_height; format=GL.GL_RGBA, type=GL.GL_UNSIGNED_BYTE) id = GL.GLuint(0) @c GL.glGenTextures(1, &id) GL.glBindTexture(GL.GL_TEXTURE_2D, id) @@ -36,19 +36,22 @@ function CImGui._create_image_texture(::Val{:GlfwOpenGL3}, image_width, image_he return Int(id) end -function CImGui._update_image_texture(::Val{:GlfwOpenGL3}, id, image_data, image_width, image_height; format=GL.GL_RGBA, type=GL.GL_UNSIGNED_BYTE) +function ig._update_image_texture(::Val{:GlfwOpenGL3}, id, image_data, image_width, image_height; format=GL.GL_RGBA, type=GL.GL_UNSIGNED_BYTE) GL.glBindTexture(GL.GL_TEXTURE_2D, g_ImageTexture[id]) GL.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, GL.GLsizei(image_width), GL.GLsizei(image_height), format, type, image_data) end -function CImGui._destroy_image_texture(::Val{:GlfwOpenGL3}, id) +function ig._destroy_image_texture(::Val{:GlfwOpenGL3}, id) id = g_ImageTexture[id] @c GL.glDeleteTextures(1, &id) delete!(g_ImageTexture, id) return true end -function CImGui._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; +_window::Union{Nothing, GLFW.Window} = nothing +ig._current_window(::Val{:GlfwOpenGL3}) = _window + +function ig._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; hotloading=true, on_exit=nothing, clear_color=Cfloat[0.45, 0.55, 0.60, 1.00], @@ -65,6 +68,8 @@ function CImGui._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; # Configure GLFW glsl_version = get_glsl_version(opengl_version) + GLFW.WindowHint(GLFW.VISIBLE, true) + GLFW.WindowHint(GLFW.DECORATED, true) GLFW.WindowHint(GLFW.CONTEXT_VERSION_MAJOR, opengl_version.major) GLFW.WindowHint(GLFW.CONTEXT_VERSION_MINOR, opengl_version.minor) @@ -75,11 +80,12 @@ function CImGui._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; # Start the test engine, if we have one if !isnothing(engine) - CImGui._start_test_engine(engine, ctx) + ig._start_test_engine(engine, ctx) end # Create window - window = GLFW.CreateWindow(window_size[1], window_size[2], window_title) + global _window = GLFW.CreateWindow(window_size[1], window_size[2], window_title) + window = _window @assert window != C_NULL GLFW.MakeContextCurrent(window) GLFW.SwapInterval(1) # enable vsync @@ -95,7 +101,7 @@ function CImGui._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; # Start the Dear ImGui frame lib.ImGui_ImplOpenGL3_NewFrame() lib.ImGui_ImplGlfw_NewFrame() - CImGui.NewFrame() + ig.NewFrame() result = if hotloading @invokelatest ui() @@ -104,18 +110,18 @@ function CImGui._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; end if !isnothing(engine) && engine.show_test_window - CImGui._show_test_window(engine) + ig._show_test_window(engine) end tests_completed = (!isnothing(engine) && engine.exit_on_completion - && !CImGui._test_engine_is_running(engine)) + && !ig._test_engine_is_running(engine)) if result === :imgui_exit_loop || tests_completed GLFW.SetWindowShouldClose(window, true) end # Rendering - CImGui.Render() + ig.Render() GLFW.MakeContextCurrent(window) display_w, display_h = GLFW.GetFramebufferSize(window) @@ -123,7 +129,7 @@ function CImGui._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; GL.glViewport(0, 0, display_w, display_h) GL.glClearColor((clear_color isa Ref ? clear_color[] : clear_color)...) GL.glClear(GL.GL_COLOR_BUFFER_BIT) - lib.ImGui_ImplOpenGL3_RenderDrawData(Ptr{Cint}(CImGui.GetDrawData())) + lib.ImGui_ImplOpenGL3_RenderDrawData(Ptr{Cint}(ig.GetDrawData())) GLFW.MakeContextCurrent(window) GLFW.SwapBuffers(window) @@ -136,19 +142,19 @@ function CImGui._render(ui, ctx::Ptr{lib.ImGuiContext}, ::Val{:GlfwOpenGL3}; end end catch e - @error "Error in CImGui $(CImGui._backend[]) renderloop!" exception=(e, catch_backtrace()) + @error "Error in CImGui $(ig._backend[]) renderloop!" exception=(e, catch_backtrace()) finally - if !isnothing(on_exit) + for func in vcat(ig._exit_handlers, isnothing(on_exit) ? [] : [on_exit]) try - on_exit() - catch exit_ex - @error "Error in on_exit()!" exception=exit_ex + func() + catch ex + @error "Error in exit handler!" exception=(ex, catch_backtrace()) end end lib.ImGui_ImplOpenGL3_Shutdown() lib.ImGui_ImplGlfw_Shutdown() - CImGui.DestroyContext(ctx) + ig.DestroyContext(ctx) GLFW.DestroyWindow(window) end end diff --git a/ext/MakieIntegration.jl b/ext/MakieIntegration.jl new file mode 100644 index 0000000..9cf247e --- /dev/null +++ b/ext/MakieIntegration.jl @@ -0,0 +1,201 @@ +module MakieIntegration + +import CImGui as ig +import ModernGL as gl +import GLFW +import GLMakie +import GLMakie.Makie as Makie + + +# Represents a single Figure to be shown as an ImGui image texture +struct ImMakieWindow + glfw_window::GLFW.Window # Only needed for supporting GLMakie requirements +end + +struct ImMakieFigure + figure::GLMakie.Figure + screen::GLMakie.Screen{ImMakieWindow} +end + +const makie_context = Dict{ig.ImGuiID, ImMakieFigure}() + +function destroy_context() + for imfigure in values(makie_context) + empty!(imfigure.figure) + end + + empty!(makie_context) +end + +Base.isopen(window::ImMakieWindow) = isopen(window.glfw_window) + +# Specialization of Base.resize(::GLMakie.Screen, ::Int, ::Int) to not do GLFW things +# See: https://github.com/MakieOrg/Makie.jl/blob/4c4eaa1f3a7f7b3777a4b8ab38388a48c0eee6ce/GLMakie/src/screen.jl#L664 +function Base.resize!(screen::GLMakie.Screen{ImMakieWindow}, w::Int, h::Int) + fbscale = screen.px_per_unit[] + fbw, fbh = round.(Int, fbscale .* (w, h)) + resize!(screen.framebuffer, fbw, fbh) +end + +# Not sure if this is correct, it should probably be the figure size +GLMakie.framebuffer_size(window::ImMakieWindow) = GLMakie.framebuffer_size(window.glfw_window) + +# ShaderAbstractions support +GLMakie.ShaderAbstractions.native_switch_context!(x::ImMakieWindow) = GLFW.MakeContextCurrent(x.glfw_window) +GLMakie.ShaderAbstractions.native_context_alive(x::ImMakieWindow) = GLFW.is_initialized() && x.glfw_window != C_NULL + +# This is called by GLMakie.display() to set up connections to GLFW for +# mouse/keyboard events etc. We disable it explicitly because we deliver the +# events in an immediate-mode fashion within MakieFigure(). +GLMakie.connect_screen(::GLMakie.Scene, ::GLMakie.Screen{ImMakieWindow}) = nothing + +# Modified copy of apply_config!() with all GLFW/renderloop things removed +# See: https://github.com/MakieOrg/Makie.jl/blob/4c4eaa1f3a7f7b3777a4b8ab38388a48c0eee6ce/GLMakie/src/screen.jl#L343 +function apply_config!(screen::GLMakie.Screen, config::GLMakie.ScreenConfig) + screen.scalefactor[] = !isnothing(config.scalefactor) ? config.scalefactor : 1 + screen.px_per_unit[] = !isnothing(config.px_per_unit) ? config.px_per_unit : screen.scalefactor[] + function replace_processor!(postprocessor, idx) + fb = screen.framebuffer + shader_cache = screen.shader_cache + post = screen.postprocessors[idx] + if post.constructor !== postprocessor + GLMakie.destroy!(screen.postprocessors[idx]) + screen.postprocessors[idx] = postprocessor(fb, shader_cache) + end + + nothing + end + + replace_processor!(config.ssao ? GLMakie.ssao_postprocessor : GLMakie.empty_postprocessor, 1) + replace_processor!(config.oit ? GLMakie.OIT_postprocessor : GLMakie.empty_postprocessor, 2) + replace_processor!(config.fxaa ? GLMakie.fxaa_postprocessor : GLMakie.empty_postprocessor, 3) + + # Set the config + screen.config = config +end + +function ig.MakieFigure(title_id::String, f::GLMakie.Figure; auto_resize_x=true, auto_resize_y=false) + ig.PushID(title_id) + id = ig.GetID(title_id) + + if !haskey(makie_context, id) + # The code in this block combines the screen creation code from + # GLMakie.empty_screen() and the screen configuration code from + # GLMakie.Screen(). + # See: + # - https://github.com/MakieOrg/Makie.jl/blob/4c4eaa1f3a7f7b3777a4b8ab38388a48c0eee6ce/GLMakie/src/screen.jl#L223 + # - https://github.com/MakieOrg/Makie.jl/blob/4c4eaa1f3a7f7b3777a4b8ab38388a48c0eee6ce/GLMakie/src/screen.jl#L388 + window = ig.current_window() + makie_window = ImMakieWindow(window) + GLMakie.ShaderAbstractions.switch_context!(makie_window) + shader_cache = GLMakie.GLAbstraction.ShaderCache(makie_window) + + fb = GLMakie.GLFramebuffer((10, 10)) + gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0) # Have to unbind after creating the framebuffer + postprocessors = [ + GLMakie.empty_postprocessor(), + GLMakie.empty_postprocessor(), + GLMakie.empty_postprocessor(), + GLMakie.to_screen_postprocessor(fb, shader_cache) + ] + + screen = GLMakie.Screen(makie_window, shader_cache, fb, + nothing, false, + nothing, + Dict{WeakRef, GLMakie.ScreenID}(), + GLMakie.ScreenArea[], + Tuple{GLMakie.ZIndex, GLMakie.ScreenID, GLMakie.RenderObject}[], + postprocessors, + Dict{UInt64, GLMakie.RenderObject}(), + Dict{UInt32, GLMakie.AbstractPlot}(), + true) + config = Makie.merge_screen_config(GLMakie.ScreenConfig, Dict{Symbol, Any}()) + apply_config!(screen, config) + + makie_context[id] = ImMakieFigure(f, screen) + scene = Makie.get_scene(f) + scene.events.window_open[] = true + display(screen, f) + end + + imfigure = makie_context[id] + scene = Makie.get_scene(f) + + region_avail = ig.GetContentRegionAvail() + region_size = (Int(region_avail.x), Int(region_avail.y)) + scene_size = size(scene) + new_size = (auto_resize_x ? region_size[1] : scene_size[1], + auto_resize_y ? region_size[2] : scene_size[2]) + + if scene_size != new_size && all(new_size .> 0) + @debug "resizing $(scene_size) -> $(new_size)" + scene.events.window_area[] = GLMakie.Rect2i(0, 0, Int(new_size[1]), Int(new_size[2])) + resize!(f, new_size[1], new_size[2]) + end + + do_render = GLMakie.requires_update(imfigure.screen) + if do_render + @debug "rendering" + GLMakie.render_frame(imfigure.screen) + end + + # The color texture is what we need to render as an image. We add it to the + # drawlist and then create an InvisibleButton of the same size to create a + # space in the layout that can respond to key presses and clicks etc (which + # a regular Image() can't do). + color_buffer = imfigure.screen.framebuffer.buffers[:color] + drawlist = ig.GetWindowDrawList() + cursor_pos = ig.GetCursorScreenPos() + image_size = size(color_buffer) + ig.AddImage(drawlist, + Ptr{Cvoid}(Int(color_buffer.id)), + cursor_pos, + (cursor_pos.x + image_size[1], cursor_pos.y + image_size[2]), + (0, 1), (1, 0)) + ig.InvisibleButton("figure_image", size(color_buffer)) + + # Update the scene events + if scene.events.hasfocus[] != ig.IsItemHovered() + scene.events.hasfocus[] = ig.IsItemHovered() + end + if scene.events.entered_window[] != ig.IsItemHovered() + scene.events.entered_window[] = ig.IsItemHovered() + end + + io = ig.GetIO() + if ig.IsItemHovered() + pos = ig.GetMousePos() + cursor_pos = ig.GetCursorScreenPos() + item_spacing = unsafe_load(ig.GetStyle().ItemSpacing.y) + new_pos = (pos.x - cursor_pos.x, abs(pos.y - cursor_pos.y) - item_spacing) + if new_pos != scene.events.mouseposition[] + scene.events.mouseposition[] = new_pos + end + + for (igkey, makiekey) in ((ig.ImGuiKey_MouseLeft, Makie.Mouse.left), + (ig.ImGuiKey_MouseRight, Makie.Mouse.right)) + if ig.IsKeyPressed(igkey) + scene.events.mousebutton[] = Makie.MouseButtonEvent(makiekey, Makie.Mouse.press) + elseif ig.IsKeyReleased(igkey) + scene.events.mousebutton[] = Makie.MouseButtonEvent(makiekey, Makie.Mouse.release) + end + end + + wheel_y = unsafe_load(io.MouseWheel) + wheel_x = unsafe_load(io.MouseWheelH) + if (wheel_x, wheel_y) != scene.events.scroll[] + scene.events.scroll[] = (wheel_x, wheel_y) + end + + end + + ig.PopID() + + return do_render +end + +function __init__() + ig.atrenderexit(destroy_context) +end + +end diff --git a/src/CImGui.jl b/src/CImGui.jl index fbd06e3..c1627d1 100644 --- a/src/CImGui.jl +++ b/src/CImGui.jl @@ -65,13 +65,59 @@ include("wrapper.jl") const IMGUI_VERSION = unsafe_string(GetVersion()) +# This is implemented by the MakieIntegration extension but we document it here +# so that we don't have to install GLMakie to build the docs. +""" + MakieFigure(id::String, f::GLMakie.Figure; auto_resize_x=true, auto_resize_y=false) + +Display a Makie figure in ImGui. See `examples/makie_demo.jl` for an example of +how to use it. This supports all the interaction features in GLMakie: +- Scrolling to zoom +- Rectangle select to zoom +- RMB to pan + +Note that scrolling to zoom will also cause the ImGui window to scroll, which +can be annoying. This may be fixed in the future by using some other key +combination for scrolling to zoom. + +These are the [interaction +events](https://docs.makie.org/stable/explanations/events#The-Events-struct) +that are wired up and can be used: +- `hasfocus` +- `entered_window` +- `mousebutton` +- `mouseposition` + +Known issues: +- Changing tick labels don't trigger the scene to be re-layouted, causing them + to be clipped if the labels change width. See `examples/makie_demo.jl` for an + example workaround using `Makie.tight_ticklabel_spacing!()`. +- The theming doesn't match the ImGui theme so plots look quite out of place by + default. +- Mouse events aren't delivered unless the mouse is hovered over the figure, so + dragging the mouse from within the figure to somewhere outside the figure will + keep the old mouse state. e.g. if you're RMB panning and the mouse goes + outside the figure, when it enters the figure again panning will resume even + the RMB was released. + +!!! warning + This is very experimental, you will almost definitely encounter bugs (and if + so please submit an issue/PR). We don't consider this covered under semver + yet so there may be breaking changes in minor releases. +""" +function MakieFigure end + ## Backends +const _exit_handlers = Function[] +atrenderexit(f::Function) = push!(_exit_handlers, f) + # These are backend functions that must be implemented by package extensions function _render end function _create_image_texture end function _update_image_texture end function _destroy_image_texture end +function _current_window end const _backend = Ref{Symbol}() @@ -133,6 +179,11 @@ function render(args...; kwargs...) _render(args..., Val(_backend[]); kwargs...) end +function current_window() + _check_backend() + _current_window(Val(_backend[])) +end + function create_image_texture(args...; kwargs...) _check_backend() _create_image_texture(Val(_backend[]), args...; kwargs...) @@ -162,6 +213,10 @@ function __init__() if isempty(methods(exc.f)) print(io, "\nrender() cannot be called yet. You must load the packages for supported backends, e.g. `import ModernGL, GLFW` for the GLFW/OpenGL3 backend.") end + elseif exc.f === MakieFigure + if isempty(methods(exc.f)) + print(io, "\nMakieFigure() cannot be called yet, you must load GLMakie with e.g. `import GLMakie`.") + end end end end diff --git a/test/Project.toml b/test/Project.toml index e383b09..3905346 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,6 +1,7 @@ [deps] CImGui = "5d785b6c-b76f-510e-a07c-3070796c7e87" GLFW = "f7f18e0c-5ee9-5ccd-a5bf-e8befd85ed98" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" ImGuiTestEngine = "464e2eba-0a11-4ed3-b274-413caa1a1cca" ModernGL = "66fc600b-dfda-50eb-8b99-91cfa97b1301" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" diff --git a/test/runtests.jl b/test/runtests.jl index a49c314..a5edb73 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,8 +4,6 @@ using ImGuiTestEngine import ImGuiTestEngine as te import ModernGL, GLFW -ig.set_backend(:GlfwOpenGL3) - include(joinpath(@__DIR__, "../demo/demo.jl")) @@ -69,3 +67,17 @@ include(joinpath(@__DIR__, "../examples/demo.jl")) te.DestroyContext(engine) end + +include(joinpath(@__DIR__, "../examples/makie_demo.jl")) + +@testset "MakieFigure" begin + engine = te.CreateContext() + + @register_test(engine, "Makie demo", "Simple plot") do ctx + SetRef("Makie demo") + ItemClick("Random data") + end + + makie_demo(; engine) + te.DestroyContext(engine) +end