Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_opengl3.cpp
#	backends/imgui_impl_osx.h
#	backends/imgui_impl_osx.mm
#	imgui.cpp
This commit is contained in:
ocornut 2021-12-15 12:10:03 +01:00
commit cd36acc88b
17 changed files with 398 additions and 166 deletions

View File

@ -37,14 +37,10 @@ jobs:
- name: Fix Projects - name: Fix Projects
shell: powershell shell: powershell
run: | run: |
# WARNING: This will need updating if toolset/sdk change in project files! # CI workers do not supporter older Visual Studio versions. Fix projects to target newer available version.
gci -recurse -filter "*.vcxproj" | ForEach-Object { gci -recurse -filter "*.vcxproj" | ForEach-Object {
# Fix SDK and toolset for most samples. (Get-Content $_.FullName) -Replace "<PlatformToolset>v\d{3}</PlatformToolset>","<PlatformToolset>v142</PlatformToolset>" | Set-Content -Path $_.FullName
(Get-Content $_.FullName) -Replace "<PlatformToolset>v110</PlatformToolset>","<PlatformToolset>v142</PlatformToolset>" | Set-Content -Path $_.FullName (Get-Content $_.FullName) -Replace "<WindowsTargetPlatformVersion>[\d\.]+</WindowsTargetPlatformVersion>","<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>" | Set-Content -Path $_.FullName
(Get-Content $_.FullName) -Replace "<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>","<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>" | Set-Content -Path $_.FullName
# Fix SDK and toolset for samples that require newer SDK/toolset. At the moment it is only dx12.
(Get-Content $_.FullName) -Replace "<PlatformToolset>v140</PlatformToolset>","<PlatformToolset>v142</PlatformToolset>" | Set-Content -Path $_.FullName
(Get-Content $_.FullName) -Replace "<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>","<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>" | Set-Content -Path $_.FullName
} }
# Not using matrix here because it would inflate job count too much. Check out and setup is done for every job and that makes build times way too long. # Not using matrix here because it would inflate job count too much. Check out and setup is done for every job and that makes build times way too long.
@ -497,34 +493,3 @@ jobs:
run: | run: |
cd examples/example_android_opengl3/android cd examples/example_android_opengl3/android
gradle assembleDebug gradle assembleDebug
Discord-CI:
runs-on: ubuntu-18.04
if: always()
needs: [Windows, Linux, MacOS, iOS, Emscripten, Android]
steps:
- uses: dearimgui/github_discord_notifier@latest
with:
discord-webhook: ${{ secrets.DISCORD_CI_WEBHOOK }}
github-token: ${{ github.token }}
action-task: discord-jobs
discord-filter: "'{{ github.branch }}'.match(/master|docking/g) != null && '{{ run.conclusion }}' != '{{ last_run.conclusion }}'"
discord-username: GitHub Actions
discord-job-new-failure-message: ''
discord-job-fixed-failure-message: ''
discord-job-new-failure-embed: |
{
"title": "`{{ job.name }}` job is failing on `{{ github.branch }}`!",
"description": "Commit [{{ github.context.payload.head_commit.title }}]({{ github.context.payload.head_commit.url }}) pushed to [{{ github.branch }}]({{ github.branch_url }}) broke [{{ job.name }}]({{ job.url }}) build job.\nFailing steps: {{ failing_steps }}",
"url": "{{ job.url }}",
"color": "0xFF0000",
"timestamp": "{{ run.updated_at }}"
}
discord-job-fixed-failure-embed: |
{
"title": "`{{ github.branch }}` branch is no longer failing!",
"description": "Build failures were fixed on [{{ github.branch }}]({{ github.branch_url }}) branch.",
"color": "0x00FF00",
"url": "{{ github.context.payload.head_commit.url }}",
"timestamp": "{{ run.completed_at }}"
}

View File

@ -16,6 +16,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions. // 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader. // 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
@ -176,6 +177,8 @@ struct ImGui_ImplOpenGL3_Data
GLuint AttribLocationVtxUV; GLuint AttribLocationVtxUV;
GLuint AttribLocationVtxColor; GLuint AttribLocationVtxColor;
unsigned int VboHandle, ElementsHandle; unsigned int VboHandle, ElementsHandle;
GLsizeiptr VertexBufferSize;
GLsizeiptr IndexBufferSize;
bool HasClipOrigin; bool HasClipOrigin;
ImGui_ImplOpenGL3_Data() { memset(this, 0, sizeof(*this)); } ImGui_ImplOpenGL3_Data() { memset(this, 0, sizeof(*this)); }
@ -436,8 +439,20 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers // Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
if (bd->VertexBufferSize < vtx_buffer_size)
{
bd->VertexBufferSize = vtx_buffer_size;
glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW);
}
if (bd->IndexBufferSize < idx_buffer_size)
{
bd->IndexBufferSize = idx_buffer_size;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW);
}
glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{ {

View File

@ -249,11 +249,13 @@ typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#ifdef GL_GLEXT_PROTOTYPES #ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer);
GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers);
GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers);
GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#endif #endif
#endif /* GL_VERSION_1_5 */ #endif /* GL_VERSION_1_5 */
#ifndef GL_VERSION_2_0 #ifndef GL_VERSION_2_0
@ -447,6 +449,7 @@ union GL3WProcs {
PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate; PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate;
PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate; PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate;
PFNGLBUFFERDATAPROC BufferData; PFNGLBUFFERDATAPROC BufferData;
PFNGLBUFFERSUBDATAPROC BufferSubData;
PFNGLCLEARPROC Clear; PFNGLCLEARPROC Clear;
PFNGLCLEARCOLORPROC ClearColor; PFNGLCLEARCOLORPROC ClearColor;
PFNGLCOMPILESHADERPROC CompileShader; PFNGLCOMPILESHADERPROC CompileShader;
@ -506,6 +509,7 @@ GL3W_API extern union GL3WProcs imgl3wProcs;
#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate #define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate
#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate #define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate
#define glBufferData imgl3wProcs.gl.BufferData #define glBufferData imgl3wProcs.gl.BufferData
#define glBufferSubData imgl3wProcs.gl.BufferSubData
#define glClear imgl3wProcs.gl.Clear #define glClear imgl3wProcs.gl.Clear
#define glClearColor imgl3wProcs.gl.ClearColor #define glClearColor imgl3wProcs.gl.ClearColor
#define glCompileShader imgl3wProcs.gl.CompileShader #define glCompileShader imgl3wProcs.gl.CompileShader
@ -692,6 +696,7 @@ static const char *proc_names[] = {
"glBlendEquationSeparate", "glBlendEquationSeparate",
"glBlendFuncSeparate", "glBlendFuncSeparate",
"glBufferData", "glBufferData",
"glBufferSubData",
"glClear", "glClear",
"glClearColor", "glClearColor",
"glCompileShader", "glCompileShader",

View File

@ -5,8 +5,9 @@
// Implemented features: // Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space).
// Issues: // Issues:
// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters]..
// [ ] Platform: Multi-viewport / platform windows. // [ ] Platform: Multi-viewport / platform windows.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@ -19,7 +20,7 @@
@class NSEvent; @class NSEvent;
@class NSView; @class NSView;
IMGUI_IMPL_API bool ImGui_ImplOSX_Init(); IMGUI_IMPL_API bool ImGui_ImplOSX_Init(NSView* _Nonnull view);
IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view); IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view);
IMGUI_IMPL_API bool ImGui_ImplOSX_HandleEvent(NSEvent* _Nonnull event, NSView* _Nullable view); IMGUI_IMPL_API bool ImGui_ImplOSX_HandleEvent(NSEvent* _Nonnull event, NSView* _Nullable view);

View File

@ -5,8 +5,9 @@
// Implemented features: // Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space).
// Issues: // Issues:
// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters]..
// [ ] Platform: Multi-viewport / platform windows. // [ ] Platform: Multi-viewport / platform windows.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@ -14,13 +15,17 @@
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs // Read online: https://github.com/ocornut/imgui/tree/master/docs
#include "imgui.h" #import "imgui.h"
#include "imgui_impl_osx.h" #import "imgui_impl_osx.h"
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include <mach/mach_time.h> #import <mach/mach_time.h>
#import <Carbon/Carbon.h>
#import <GameController/GameController.h>
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys.
// 2021-12-13: Add game controller support.
// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards.
// 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. // 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events.
// 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. // 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key.
@ -38,6 +43,7 @@
// 2018-07-07: Initial version. // 2018-07-07: Initial version.
@class ImFocusObserver; @class ImFocusObserver;
@class KeyEventResponder;
// Data // Data
static double g_HostClockPeriod = 0.0; static double g_HostClockPeriod = 0.0;
@ -46,7 +52,8 @@ static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
static bool g_MouseCursorHidden = false; static bool g_MouseCursorHidden = false;
static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {};
static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; static bool g_MouseDown[ImGuiMouseButton_COUNT] = {};
static ImFocusObserver* g_FocusObserver = NULL; static ImFocusObserver* g_FocusObserver = nil;
static KeyEventResponder* g_KeyEventResponder = nil;
// Undocumented methods for creating cursors. // Undocumented methods for creating cursors.
@interface NSCursor() @interface NSCursor()
@ -75,6 +82,102 @@ static void resetKeys()
io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false;
} }
/**
KeyEventResponder implements the NSTextInputClient protocol as is required by the macOS text input manager.
The macOS text input manager is invoked by calling the interpretKeyEvents method from the keyDown method.
Keyboard events are then evaluated by the macOS input manager and valid text input is passed back via the
insertText:replacementRange method.
This is the same approach employed by other cross-platform libraries such as SDL2:
https://github.com/spurious/SDL-mirror/blob/e17aacbd09e65a4fd1e166621e011e581fb017a8/src/video/cocoa/SDL_cocoakeyboard.m#L53
and GLFW:
https://github.com/glfw/glfw/blob/b55a517ae0c7b5127dffa79a64f5406021bf9076/src/cocoa_window.m#L722-L723
*/
@interface KeyEventResponder: NSView<NSTextInputClient>
@end
@implementation KeyEventResponder
- (void)viewDidMoveToWindow
{
// Ensure self is a first responder to receive the input events.
[self.window makeFirstResponder:self];
}
- (void)keyDown:(NSEvent*)event
{
// Call to the macOS input manager system.
[self interpretKeyEvents:@[event]];
}
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
{
ImGuiIO& io = ImGui::GetIO();
NSString* characters;
if ([aString isKindOfClass:[NSAttributedString class]])
characters = [aString string];
else
characters = (NSString*)aString;
io.AddInputCharactersUTF8(characters.UTF8String);
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (void)doCommandBySelector:(SEL)myselector
{
}
- (nullable NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange
{
return nil;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)point
{
return 0;
}
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange
{
return NSZeroRect;
}
- (BOOL)hasMarkedText
{
return NO;
}
- (NSRange)markedRange
{
return NSMakeRange(NSNotFound, 0);
}
- (NSRange)selectedRange
{
return NSMakeRange(NSNotFound, 0);
}
- (void)setMarkedText:(nonnull id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
}
- (void)unmarkText
{
}
- (nonnull NSArray<NSAttributedStringKey>*)validAttributesForMarkedText
{
return @[];
}
@end
@interface ImFocusObserver : NSObject @interface ImFocusObserver : NSObject
- (void)onApplicationBecomeActive:(NSNotification*)aNotification; - (void)onApplicationBecomeActive:(NSNotification*)aNotification;
@ -104,7 +207,7 @@ static void resetKeys()
@end @end
// Functions // Functions
bool ImGui_ImplOSX_Init() bool ImGui_ImplOSX_Init(NSView* view)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
@ -116,29 +219,28 @@ bool ImGui_ImplOSX_Init()
io.BackendPlatformName = "imgui_impl_osx"; io.BackendPlatformName = "imgui_impl_osx";
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array. // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array.
const int offset_for_function_keys = 256 - 0xF700; io.KeyMap[ImGuiKey_Tab] = kVK_Tab;
io.KeyMap[ImGuiKey_Tab] = '\t'; io.KeyMap[ImGuiKey_LeftArrow] = kVK_LeftArrow;
io.KeyMap[ImGuiKey_LeftArrow] = NSLeftArrowFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_RightArrow] = kVK_RightArrow;
io.KeyMap[ImGuiKey_RightArrow] = NSRightArrowFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_UpArrow] = kVK_UpArrow;
io.KeyMap[ImGuiKey_UpArrow] = NSUpArrowFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_DownArrow] = kVK_DownArrow;
io.KeyMap[ImGuiKey_DownArrow] = NSDownArrowFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_PageUp] = kVK_PageUp;
io.KeyMap[ImGuiKey_PageUp] = NSPageUpFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_PageDown] = kVK_PageDown;
io.KeyMap[ImGuiKey_PageDown] = NSPageDownFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_Home] = kVK_Home;
io.KeyMap[ImGuiKey_Home] = NSHomeFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_End] = kVK_End;
io.KeyMap[ImGuiKey_End] = NSEndFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_Insert] = kVK_F13;
io.KeyMap[ImGuiKey_Insert] = NSInsertFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_Delete] = kVK_ForwardDelete;
io.KeyMap[ImGuiKey_Delete] = NSDeleteFunctionKey + offset_for_function_keys; io.KeyMap[ImGuiKey_Backspace] = kVK_Delete;
io.KeyMap[ImGuiKey_Backspace] = 127; io.KeyMap[ImGuiKey_Space] = kVK_Space;
io.KeyMap[ImGuiKey_Space] = 32; io.KeyMap[ImGuiKey_Enter] = kVK_Return;
io.KeyMap[ImGuiKey_Enter] = 13; io.KeyMap[ImGuiKey_Escape] = kVK_Escape;
io.KeyMap[ImGuiKey_Escape] = 27; io.KeyMap[ImGuiKey_KeyPadEnter] = kVK_ANSI_KeypadEnter;
io.KeyMap[ImGuiKey_KeyPadEnter] = 3; io.KeyMap[ImGuiKey_A] = kVK_ANSI_A;
io.KeyMap[ImGuiKey_A] = 'A'; io.KeyMap[ImGuiKey_C] = kVK_ANSI_C;
io.KeyMap[ImGuiKey_C] = 'C'; io.KeyMap[ImGuiKey_V] = kVK_ANSI_V;
io.KeyMap[ImGuiKey_V] = 'V'; io.KeyMap[ImGuiKey_X] = kVK_ANSI_X;
io.KeyMap[ImGuiKey_X] = 'X'; io.KeyMap[ImGuiKey_Y] = kVK_ANSI_Y;
io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Z] = kVK_ANSI_Z;
io.KeyMap[ImGuiKey_Z] = 'Z';
// Load cursors. Some of them are undocumented. // Load cursors. Some of them are undocumented.
g_MouseCursorHidden = false; g_MouseCursorHidden = false;
@ -191,6 +293,11 @@ bool ImGui_ImplOSX_Init()
name:NSApplicationDidResignActiveNotification name:NSApplicationDidResignActiveNotification
object:nil]; object:nil];
// Add the NSTextInputClient to the view hierarchy,
// to receive keyboard events and translate them to input text.
g_KeyEventResponder = [[KeyEventResponder alloc] initWithFrame:NSZeroRect];
[view addSubview:g_KeyEventResponder];
return true; return true;
} }
@ -225,8 +332,12 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons()
} }
else else
{ {
// Show OS mouse cursor NSCursor* desired = g_MouseCursors[imgui_cursor] ?: g_MouseCursors[ImGuiMouseCursor_Arrow];
[g_MouseCursors[g_MouseCursors[imgui_cursor] ? imgui_cursor : ImGuiMouseCursor_Arrow] set]; // -[NSCursor set] generates measureable overhead if called unconditionally.
if (desired != NSCursor.currentCursor)
{
[desired set];
}
if (g_MouseCursorHidden) if (g_MouseCursorHidden)
{ {
g_MouseCursorHidden = false; g_MouseCursorHidden = false;
@ -235,6 +346,50 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons()
} }
} }
void ImGui_ImplOSX_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
GCController* controller;
if (@available(macOS 11.0, *))
controller = GCController.current;
else
controller = GCController.controllers.firstObject;
if (controller == nil || controller.extendedGamepad == nil)
{
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
return;
}
GCExtendedGamepad* gp = controller.extendedGamepad;
#define MAP_BUTTON(NAV_NO, NAME) { io.NavInputs[NAV_NO] = gp.NAME.isPressed ? 1.0 : 0.0; }
MAP_BUTTON(ImGuiNavInput_Activate, buttonA);
MAP_BUTTON(ImGuiNavInput_Cancel, buttonB);
MAP_BUTTON(ImGuiNavInput_Menu, buttonX);
MAP_BUTTON(ImGuiNavInput_Input, buttonY);
MAP_BUTTON(ImGuiNavInput_DpadLeft, dpad.left);
MAP_BUTTON(ImGuiNavInput_DpadRight, dpad.right);
MAP_BUTTON(ImGuiNavInput_DpadUp, dpad.up);
MAP_BUTTON(ImGuiNavInput_DpadDown, dpad.down);
MAP_BUTTON(ImGuiNavInput_FocusPrev, leftShoulder);
MAP_BUTTON(ImGuiNavInput_FocusNext, rightShoulder);
MAP_BUTTON(ImGuiNavInput_TweakSlow, leftTrigger);
MAP_BUTTON(ImGuiNavInput_TweakFast, rightTrigger);
#undef MAP_BUTTON
io.NavInputs[ImGuiNavInput_LStickLeft] = gp.leftThumbstick.left.value;
io.NavInputs[ImGuiNavInput_LStickRight] = gp.leftThumbstick.right.value;
io.NavInputs[ImGuiNavInput_LStickUp] = gp.leftThumbstick.up.value;
io.NavInputs[ImGuiNavInput_LStickDown] = gp.leftThumbstick.down.value;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
}
void ImGui_ImplOSX_NewFrame(NSView* view) void ImGui_ImplOSX_NewFrame(NSView* view)
{ {
// Setup display size // Setup display size
@ -257,19 +412,25 @@ void ImGui_ImplOSX_NewFrame(NSView* view)
g_Time = current_time; g_Time = current_time;
ImGui_ImplOSX_UpdateMouseCursorAndButtons(); ImGui_ImplOSX_UpdateMouseCursorAndButtons();
ImGui_ImplOSX_UpdateGamepads();
} }
static int mapCharacterToKey(int c) NSString* NSStringFromPhase(NSEventPhase phase)
{ {
if (c >= 'a' && c <= 'z') static NSString* strings[] =
return c - 'a' + 'A'; {
if (c == 25) // SHIFT+TAB -> TAB @"none",
return 9; @"began",
if (c >= 0 && c < 256) @"stationary",
return c; @"changed",
if (c >= 0xF700 && c < 0xF700 + 256) @"ended",
return c - 0xF700 + 256; @"cancelled",
return -1; @"mayBegin",
};
int pos = phase == NSEventPhaseNone ? 0 : __builtin_ctzl((NSUInteger)phase) + 1;
return strings[pos];
} }
bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
@ -302,6 +463,21 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
if (event.type == NSEventTypeScrollWheel) if (event.type == NSEventTypeScrollWheel)
{ {
// Ignore canceled events.
//
// From macOS 12.1, scrolling with two fingers and then decelerating
// by tapping two fingers results in two events appearing:
//
// 1. A scroll wheel NSEvent, with a phase == NSEventPhaseMayBegin, when the user taps
// two fingers to decelerate or stop the scroll events.
//
// 2. A scroll wheel NSEvent, with a phase == NSEventPhaseCancelled, when the user releases the
// two-finger tap. It is this event that sometimes contains large values for scrollingDeltaX and
// scrollingDeltaY. When these are added to the current x and y positions of the scrolling view,
// it appears to jump up or down. It can be observed in Preview, various JetBrains IDEs and here.
if (event.phase == NSEventPhaseCancelled)
return false;
double wheel_dx = 0.0; double wheel_dx = 0.0;
double wheel_dy = 0.0; double wheel_dy = 0.0;
@ -323,6 +499,8 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
wheel_dy = [event deltaY]; wheel_dy = [event deltaY];
} }
//NSLog(@"dx=%0.3ff, dy=%0.3f, phase=%@", wheel_dx, wheel_dy, NSStringFromPhase(event.phase));
if (fabs(wheel_dx) > 0.0) if (fabs(wheel_dx) > 0.0)
io.MouseWheelH += (float)wheel_dx * 0.1f; io.MouseWheelH += (float)wheel_dx * 0.1f;
if (fabs(wheel_dy) > 0.0) if (fabs(wheel_dy) > 0.0)
@ -330,57 +508,37 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
return io.WantCaptureMouse; return io.WantCaptureMouse;
} }
// FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm. if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp)
if (event.type == NSEventTypeKeyDown)
{ {
NSString* str = [event characters]; unsigned short code = event.keyCode;
NSUInteger len = [str length]; IM_ASSERT(code >= 0 && code < IM_ARRAYSIZE(io.KeysDown));
for (NSUInteger i = 0; i < len; i++) io.KeysDown[code] = event.type == NSEventTypeKeyDown;
{ NSEventModifierFlags flags = event.modifierFlags;
int c = [str characterAtIndex:i]; io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0;
if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) io.KeyShift = (flags & NSEventModifierFlagShift) != 0;
io.AddInputCharacter((unsigned int)c); io.KeyAlt = (flags & NSEventModifierFlagOption) != 0;
io.KeySuper = (flags & NSEventModifierFlagCommand) != 0;
// We must reset in case we're pressing a sequence of special keys while keeping the command pressed
int key = mapCharacterToKey(c);
if (key != -1 && key < 256 && !io.KeySuper)
resetKeys();
if (key != -1)
io.KeysDown[key] = true;
}
return io.WantCaptureKeyboard;
}
if (event.type == NSEventTypeKeyUp)
{
NSString* str = [event characters];
NSUInteger len = [str length];
for (NSUInteger i = 0; i < len; i++)
{
int c = [str characterAtIndex:i];
int key = mapCharacterToKey(c);
if (key != -1)
io.KeysDown[key] = false;
}
return io.WantCaptureKeyboard; return io.WantCaptureKeyboard;
} }
if (event.type == NSEventTypeFlagsChanged) if (event.type == NSEventTypeFlagsChanged)
{ {
unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; NSEventModifierFlags flags = event.modifierFlags;
switch (event.keyCode)
bool oldKeyCtrl = io.KeyCtrl; {
bool oldKeyShift = io.KeyShift; case kVK_Control:
bool oldKeyAlt = io.KeyAlt; io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0;
bool oldKeySuper = io.KeySuper; break;
io.KeyCtrl = flags & NSEventModifierFlagControl; case kVK_Shift:
io.KeyShift = flags & NSEventModifierFlagShift; io.KeyShift = (flags & NSEventModifierFlagShift) != 0;
io.KeyAlt = flags & NSEventModifierFlagOption; break;
io.KeySuper = flags & NSEventModifierFlagCommand; case kVK_Option:
io.KeyAlt = (flags & NSEventModifierFlagOption) != 0;
// We must reset them as we will not receive any keyUp event if they where pressed with a modifier break;
if ((oldKeyShift && !io.KeyShift) || (oldKeyCtrl && !io.KeyCtrl) || (oldKeyAlt && !io.KeyAlt) || (oldKeySuper && !io.KeySuper)) case kVK_Command:
resetKeys(); io.KeySuper = (flags & NSEventModifierFlagCommand) != 0;
break;
}
return io.WantCaptureKeyboard; return io.WantCaptureKeyboard;
} }

View File

@ -107,6 +107,9 @@ Breaking Changes:
- Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. - Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges.
Please open an issue if you think you really need this function. (#3841) Please open an issue if you think you really need this function. (#3841)
- Backends: OSX: Added NSView* parameter to ImGui_ImplOSX_Init(). (#4759) [@stuartcarnie]
Updated Apple+Metal and Apple+GL example applications accordingly.
Other Changes: Other Changes:
@ -134,6 +137,10 @@ Other Changes:
- Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using - Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using
Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when
already at edge) reactivates the navigation highlight. already at edge) reactivates the navigation highlight.
- Menus: fixed closing a menu inside a popup/modal by clicking on the popup/modal. (#3496, #4797)
- Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) [@xndcn]
- Menus: fixed menu inside a popup/modal not inhibiting hovering of items in the popup/modal. (#3496, #4797)
- Menus: fixed sub-menu items inside a popups from closing the popup.
- InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682)
- Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761)
- InputText: made double-click select word, triple-line select line. Word delimitation logic differs - InputText: made double-click select word, triple-line select line. Word delimitation logic differs
@ -156,6 +163,8 @@ Other Changes:
- Clipper: fixed invalid state when number of frozen table row is smaller than ItemCount. - Clipper: fixed invalid state when number of frozen table row is smaller than ItemCount.
- Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose
tooltip when scrolling. (#143) tooltip when scrolling. (#143)
- Fonts: fixed infinite loop in ImFontGlyphRangesBuilder::AddRanges() when passing UINT16_MAX without
the IMGUI_USE_WCHAR32 compile-time option. (#4802) [@SlavicPotato]
- Metrics: Added a node showing windows in submission order and showing the Begin() stack. - Metrics: Added a node showing windows in submission order and showing the Begin() stack.
- Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the - Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the
io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666]
@ -165,12 +174,18 @@ Other Changes:
- Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce
likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling
vkCmdSetScissor() explicitly every frame. (#4644) vkCmdSetScissor() explicitly every frame. (#4644)
- Backends: OpenGL3: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports
with some Intel HD drivers, and perhaps improve performances. (#4468, #4504, #2981, #3381) [@parbo]
- Backends: OpenGL2, Allegro5, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. - Backends: OpenGL2, Allegro5, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field.
This is an old bug, but due to the way we created drawlists, it never had any visible side-effect before. This is an old bug, but due to the way we created drawlists, it never had any visible side-effect before.
The new code for handling Modal and CTRL+Tab dimming/whitening recently made the bug surface. (#4790) The new code for handling Modal and CTRL+Tab dimming/whitening recently made the bug surface. (#4790)
- Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775)
- Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices,
enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups]
- Backends: OSX: Generally fix keyboard support. Keyboard arrays indexed using kVK_* codes, e.g.
ImGui::IsKeyPressed(kVK_Space). Don't set mouse cursor shape unconditionally. Handle two fingers scroll
cancel event. (#4759, #4253, #1873) [@stuartcarnie]
- Backends: OSX: Add Game Controller support (need linking GameController framework) (#4759) [@stuartcarnie]
- Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and - Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and
wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the
in-flux specs says. (#4766) [@meshula] in-flux specs says. (#4766) [@meshula]

View File

@ -201,10 +201,10 @@ Ongoing Dear ImGui development is currently financially supported by users and p
- [Blizzard](https://careers.blizzard.com/en-us/openings/engineering/all/all/all/1) - [Blizzard](https://careers.blizzard.com/en-us/openings/engineering/all/all/all/1)
*Double-chocolate sponsors* *Double-chocolate sponsors*
- [Google](https://github.com/google/filament), [Nvidia](https://developer.nvidia.com/nvidia-omniverse), [Ubisoft](https://montreal.ubisoft.com/en/ubisoft-sponsors-user-interface-library-for-c-dear-imgui) - [Ubisoft](https://montreal.ubisoft.com/en/ubisoft-sponsors-user-interface-library-for-c-dear-imgui), [Supercell](https://supercell.com)
*Chocolate sponsors* *Chocolate sponsors*
- [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras Pranckevičius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [RAD Game Tools](http://www.radgametools.com/), [Supercell](https://supercell.com) - [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras Pranckevičius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [Google](https://github.com/google/filament), [Nvidia](https://developer.nvidia.com/nvidia-omniverse), [RAD Game Tools](http://www.radgametools.com/)
*Salty-caramel sponsors* *Salty-caramel sponsors*
- [Framefield](http://framefield.com), [Grinding Gear Games](https://www.grindinggear.com), [Kylotonn](https://www.kylotonn.com), [Next Level Games](https://www.nextlevelgames.com), [O-Net Communications (USA)](http://en.o-netcom.com) - [Framefield](http://framefield.com), [Grinding Gear Games](https://www.grindinggear.com), [Kylotonn](https://www.kylotonn.com), [Next Level Games](https://www.nextlevelgames.com), [O-Net Communications (USA)](http://en.o-netcom.com)

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05318E0E274C397200A8DE2E /* GameController.framework */; };
07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; };
07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; };
5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; }; 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; };
@ -32,6 +33,7 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
05318E0E274C397200A8DE2E /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
07A82ED62139413C0078D120 /* imgui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = ../../imgui_internal.h; sourceTree = "<group>"; }; 07A82ED62139413C0078D120 /* imgui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = ../../imgui_internal.h; sourceTree = "<group>"; };
07A82ED72139413C0078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = "<group>"; }; 07A82ED72139413C0078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = "<group>"; };
5079822D257677DB0038A28D /* imgui_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = ../../imgui_tables.cpp; sourceTree = "<group>"; }; 5079822D257677DB0038A28D /* imgui_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = ../../imgui_tables.cpp; sourceTree = "<group>"; };
@ -76,6 +78,7 @@
files = ( files = (
8309BDC6253CCCFE0045E2A1 /* AppKit.framework in Frameworks */, 8309BDC6253CCCFE0045E2A1 /* AppKit.framework in Frameworks */,
83BBE9EC20EB471700295997 /* MetalKit.framework in Frameworks */, 83BBE9EC20EB471700295997 /* MetalKit.framework in Frameworks */,
05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */,
83BBE9ED20EB471700295997 /* Metal.framework in Frameworks */, 83BBE9ED20EB471700295997 /* Metal.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -133,6 +136,7 @@
83BBE9E320EB46B800295997 /* Frameworks */ = { 83BBE9E320EB46B800295997 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
05318E0E274C397200A8DE2E /* GameController.framework */,
8309BDC5253CCCFE0045E2A1 /* AppKit.framework */, 8309BDC5253CCCFE0045E2A1 /* AppKit.framework */,
8309BD8E253CCAAA0045E2A1 /* UIKit.framework */, 8309BD8E253CCAAA0045E2A1 /* UIKit.framework */,
83BBE9EE20EB471C00295997 /* ModelIO.framework */, 83BBE9EE20EB471C00295997 /* ModelIO.framework */,

View File

@ -119,7 +119,7 @@
return event; return event;
}]; }];
ImGui_ImplOSX_Init(); ImGui_ImplOSX_Init(self.view);
#endif #endif
} }

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05E31B57274EF0360083FCB6 /* GameController.framework */; };
07A82EDB213941D00078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82EDA213941D00078D120 /* imgui_widgets.cpp */; }; 07A82EDB213941D00078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82EDA213941D00078D120 /* imgui_widgets.cpp */; };
4080A99820B02D340036BA46 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4080A98A20B02CD90036BA46 /* main.mm */; }; 4080A99820B02D340036BA46 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4080A98A20B02CD90036BA46 /* main.mm */; };
4080A9A220B034280036BA46 /* imgui_impl_opengl2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A99E20B034280036BA46 /* imgui_impl_opengl2.cpp */; }; 4080A9A220B034280036BA46 /* imgui_impl_opengl2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A99E20B034280036BA46 /* imgui_impl_opengl2.cpp */; };
@ -32,6 +33,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
05E31B57274EF0360083FCB6 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
07A82EDA213941D00078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = "<group>"; }; 07A82EDA213941D00078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = "<group>"; };
4080A96B20B029B00036BA46 /* example_osx_opengl2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_osx_opengl2; sourceTree = BUILT_PRODUCTS_DIR; }; 4080A96B20B029B00036BA46 /* example_osx_opengl2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_osx_opengl2; sourceTree = BUILT_PRODUCTS_DIR; };
4080A98A20B02CD90036BA46 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = SOURCE_ROOT; }; 4080A98A20B02CD90036BA46 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = SOURCE_ROOT; };
@ -57,6 +59,7 @@
files = ( files = (
4080A9B520B034EA0036BA46 /* OpenGL.framework in Frameworks */, 4080A9B520B034EA0036BA46 /* OpenGL.framework in Frameworks */,
4080A9B320B034E40036BA46 /* Cocoa.framework in Frameworks */, 4080A9B320B034E40036BA46 /* Cocoa.framework in Frameworks */,
05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -95,6 +98,7 @@
4080A9B120B034E40036BA46 /* Frameworks */ = { 4080A9B120B034E40036BA46 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
05E31B57274EF0360083FCB6 /* GameController.framework */,
4080A9B420B034EA0036BA46 /* OpenGL.framework */, 4080A9B420B034EA0036BA46 /* OpenGL.framework */,
4080A9B220B034E40036BA46 /* Cocoa.framework */, 4080A9B220B034E40036BA46 /* Cocoa.framework */,
); );

View File

@ -59,7 +59,7 @@
//ImGui::StyleColorsClassic(); //ImGui::StyleColorsClassic();
// Setup Platform/Renderer backends // Setup Platform/Renderer backends
ImGui_ImplOSX_Init(); ImGui_ImplOSX_Init(self);
ImGui_ImplOpenGL2_Init(); ImGui_ImplOpenGL2_Init();
// Load Fonts // Load Fonts
@ -147,12 +147,9 @@
animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES]; animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES];
} }
-(void)reshape { [[self openGLContext] update]; [self updateAndDrawDemoView]; } -(void)reshape { [super reshape]; [[self openGLContext] update]; [self updateAndDrawDemoView]; }
-(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; } -(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; }
-(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; } -(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; }
-(BOOL)acceptsFirstResponder { return (YES); }
-(BOOL)becomeFirstResponder { return (YES); }
-(BOOL)resignFirstResponder { return (YES); }
-(void)dealloc { animationTimer = nil; } -(void)dealloc { animationTimer = nil; }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------

View File

@ -5544,6 +5544,27 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin
window->DockOrder = settings->DockOrder; window->DockOrder = settings->DockOrder;
} }
static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
{
ImGuiContext& g = *GImGui;
const ImGuiWindowFlags old_flags = window->Flags;
const bool child_flag_changed = (new_flags & ImGuiWindowFlags_ChildWindow) != (old_flags & ImGuiWindowFlags_ChildWindow);
if ((just_created || child_flag_changed) && !(new_flags & ImGuiWindowFlags_ChildWindow))
{
g.WindowsFocusOrder.push_back(window);
window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
}
else if (child_flag_changed && (new_flags & ImGuiWindowFlags_ChildWindow))
{
IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
g.WindowsFocusOrder[n]->FocusOrder--;
g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
window->FocusOrder = -1;
}
}
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -5584,16 +5605,12 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
} }
if (!(flags & ImGuiWindowFlags_ChildWindow))
{
g.WindowsFocusOrder.push_back(window);
window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
}
if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
g.Windows.push_front(window); // Quite slow but rare and only once g.Windows.push_front(window); // Quite slow but rare and only once
else else
g.Windows.push_back(window); g.Windows.push_back(window);
UpdateWindowInFocusOrderList(window, true, window->Flags);
return window; return window;
} }
@ -6279,6 +6296,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
const bool window_just_created = (window == NULL); const bool window_just_created = (window == NULL);
if (window_just_created) if (window_just_created)
window = CreateNewWindow(name, flags); window = CreateNewWindow(name, flags);
else
UpdateWindowInFocusOrderList(window, window_just_created, flags);
// Automatically disable manual moving/resizing when NoInputs is set // Automatically disable manual moving/resizing when NoInputs is set
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
@ -6372,6 +6391,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window_stack_data.StackSizesOnBegin.SetToCurrentState(); window_stack_data.StackSizesOnBegin.SetToCurrentState();
g.CurrentWindowStack.push_back(window_stack_data); g.CurrentWindowStack.push_back(window_stack_data);
g.CurrentWindow = NULL; g.CurrentWindow = NULL;
if (flags & ImGuiWindowFlags_ChildMenu)
g.BeginMenuCount++;
if (flags & ImGuiWindowFlags_Popup) if (flags & ImGuiWindowFlags_Popup)
{ {
@ -7107,6 +7128,8 @@ void ImGui::End()
// Pop from window stack // Pop from window stack
g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
if (window->Flags & ImGuiWindowFlags_ChildMenu)
g.BeginMenuCount--;
if (window->Flags & ImGuiWindowFlags_Popup) if (window->Flags & ImGuiWindowFlags_Popup)
g.BeginPopupStack.pop_back(); g.BeginPopupStack.pop_back();
g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState();
@ -9302,7 +9325,7 @@ void ImGui::CloseCurrentPopup()
ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
bool close_parent = false; bool close_parent = false;
if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
close_parent = true; close_parent = true;
if (!close_parent) if (!close_parent)
break; break;
@ -9330,7 +9353,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
char name[20]; char name[20];
if (flags & ImGuiWindowFlags_ChildMenu) if (flags & ImGuiWindowFlags_ChildMenu)
ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth
else else
ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
@ -17583,7 +17606,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con
} }
ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
if (window && fg_draw_list && IsItemHovered()) if (window && IsItemHovered() && fg_draw_list)
fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
if (!node_open) if (!node_open)
return; return;

View File

@ -65,7 +65,7 @@ Index of this file:
// Version // Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.86 WIP" #define IMGUI_VERSION "1.86 WIP"
#define IMGUI_VERSION_NUM 18518 #define IMGUI_VERSION_NUM 18521
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE #define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch #define IMGUI_HAS_VIEWPORT // Viewport WIP branch

View File

@ -3418,11 +3418,26 @@ static void ShowDemoWindowPopups()
} }
// Call the more complete ShowExampleMenuFile which we use in various places of this demo // Call the more complete ShowExampleMenuFile which we use in various places of this demo
if (ImGui::Button("File Menu..")) if (ImGui::Button("With a menu.."))
ImGui::OpenPopup("my_file_popup"); ImGui::OpenPopup("my_file_popup");
if (ImGui::BeginPopup("my_file_popup")) if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar))
{
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("File"))
{ {
ShowExampleMenuFile(); ShowExampleMenuFile();
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Edit"))
{
ImGui::MenuItem("Dummy");
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
ImGui::Text("Hello from popup!");
ImGui::Button("This is a dummy button..");
ImGui::EndPopup(); ImGui::EndPopup();
} }

View File

@ -3088,8 +3088,8 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)
{ {
for (; ranges[0]; ranges += 2) for (; ranges[0]; ranges += 2)
for (ImWchar c = ranges[0]; c <= ranges[1]; c++) for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560
AddChar(c); AddChar((ImWchar)c);
} }
void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges) void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)

View File

@ -1758,6 +1758,7 @@ struct ImGuiContext
ImVector<ImGuiGroupData>GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVector<ImGuiGroupData>GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
ImVector<ImGuiPopupData>OpenPopupStack; // Which popups are open (persistent) ImVector<ImGuiPopupData>OpenPopupStack; // Which popups are open (persistent)
ImVector<ImGuiPopupData>BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) ImVector<ImGuiPopupData>BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame)
int BeginMenuCount;
// Viewports // Viewports
ImVector<ImGuiViewportP*> Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. ImVector<ImGuiViewportP*> Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData.
@ -1993,6 +1994,7 @@ struct ImGuiContext
LastActiveIdTimer = 0.0f; LastActiveIdTimer = 0.0f;
CurrentItemFlags = ImGuiItemFlags_None; CurrentItemFlags = ImGuiItemFlags_None;
BeginMenuCount = 0;
CurrentDpiScale = 0.0f; CurrentDpiScale = 0.0f;
CurrentViewport = NULL; CurrentViewport = NULL;

View File

@ -3703,11 +3703,11 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; }
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
#ifdef __APPLE__ // FIXME: Move setting to IO structure #ifdef __APPLE__ // FIXME: Move setting to IO structure
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC
#else #else
static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN
#endif #endif
@ -6883,6 +6883,23 @@ void ImGui::EndMainMenuBar()
End(); End();
} }
static bool IsRootOfOpenMenuSet()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu))
return false;
// Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID.
// This would however prevent the use of e.g. PuhsID() user code submitting menus.
// Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag,
// making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects.
// Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup
// doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu.
const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size];
return (/*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu));
}
bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
@ -6895,8 +6912,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
// The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) if (window->Flags & ImGuiWindowFlags_ChildMenu)
flags |= ImGuiWindowFlags_ChildWindow; flags |= ImGuiWindowFlags_ChildWindow;
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
@ -6915,11 +6933,12 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
g.MenusIdSubmittedThisFrame.push_back(id); g.MenusIdSubmittedThisFrame.push_back(id);
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, NULL, true);
bool pressed;
bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
const bool menuset_is_open = IsRootOfOpenMenuSet();
ImGuiWindow* backed_nav_window = g.NavWindow; ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open) if (menuset_is_open)
g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) g.NavWindow = window;
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
@ -6929,6 +6948,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
if (!enabled) if (!enabled)
BeginDisabled(); BeginDisabled();
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
bool pressed;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{ {
// Menu inside an horizontal menu bar // Menu inside an horizontal menu bar
@ -7088,13 +7108,19 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, NULL, true);
const bool menuset_is_open = IsRootOfOpenMenuSet();
ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open)
g.NavWindow = window;
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
bool pressed; bool pressed;
PushID(label); PushID(label);
if (!enabled) if (!enabled)
BeginDisabled(); BeginDisabled();
const ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover;
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover;
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{ {
@ -7104,7 +7130,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
pressed = Selectable("", selected, flags, ImVec2(w, 0.0f)); pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f));
PopStyleVar(); PopStyleVar();
RenderText(text_pos, label); RenderText(text_pos, label);
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
@ -7119,7 +7145,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); float checkmark_w = IM_FLOOR(g.FontSize * 1.20f);
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame
float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
pressed = Selectable("", false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label);
if (icon_w > 0.0f) if (icon_w > 0.0f)
RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
@ -7136,6 +7162,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
if (!enabled) if (!enabled)
EndDisabled(); EndDisabled();
PopID(); PopID();
if (menuset_is_open)
g.NavWindow = backed_nav_window;
return pressed; return pressed;
} }