ui: fix drag scrolling and imgui gl renderer. use imgui events

Missing call to KeepAliveID was preventing drag scrolling in empty
areas. Also check for HoveredIdDisabled to allow scrolling by dragging
disabled items and enable HoveredIdAllowOverlap.
imgui gl renderer: use ImDrawCmd::IdxOffset instead of counting
manually. Use correct alpha blending function.
Use imgui events for all input. Simplify keyboard input by getting rid
of modifiers.
Enable flat navigation in content window.
Add format string to OptionSlider.
This commit is contained in:
Flyinghead 2023-03-30 16:54:54 +02:00
parent c033a81eca
commit 08ac485eac
7 changed files with 46 additions and 33 deletions

View File

@ -109,10 +109,10 @@ protected:
if (port >= 0 && port < (int)std::size(kb_shift))
kb_shift[port] = _modifier_keys;
if (keycode != 0 && keycode < 0xE0)
if (keycode != 0)
{
gui_keyboard_key(keycode, pressed, _modifier_keys);
if (port >= 0 && port < (int)std::size(kb_key))
gui_keyboard_key(keycode, pressed);
if (keycode < 0xE0 && port >= 0 && port < (int)std::size(kb_key))
{
if (pressed)
{

View File

@ -145,7 +145,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glcache.Enable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glcache.Disable(GL_CULL_FACE);
glcache.Disable(GL_DEPTH_TEST);
glcache.Enable(GL_SCISSOR_TEST);
@ -187,6 +187,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
}
#endif
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationPosition);
glEnableVertexAttribArray(g_AttribLocationUV);
glEnableVertexAttribArray(g_AttribLocationColor);
@ -199,12 +200,8 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawIdx* idx_buffer_offset = 0;
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
@ -228,12 +225,14 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
// Bind texture, Draw
glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
idx_buffer_offset += pcmd->ElemCount;
}
}
// Make sure the state cache is coherent
glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#ifndef GLES2
if (vao_handle != 0)
glDeleteVertexArrays(1, &vao_handle);

View File

@ -152,8 +152,14 @@ static ImGuiKey keycodeToImGuiKey(u8 keycode)
case 0x1B: return ImGuiKey_X;
case 0x1C: return ImGuiKey_Y;
case 0x1D: return ImGuiKey_Z;
case 0xE0:
case 0xE4:
return ImGuiMod_Ctrl;
case 0xE1:
case 0xE5:
return ImGuiMod_Shift;
default: return ImGuiKey_None;
}
return ImGuiKey_None;
}
void gui_initFonts()
@ -295,19 +301,19 @@ void gui_keyboard_inputUTF8(const std::string& s)
io.AddInputCharactersUTF8(s.c_str());
}
void gui_keyboard_key(u8 keyCode, bool pressed, u8 modifiers)
void gui_keyboard_key(u8 keyCode, bool pressed)
{
if (!inited)
return;
ImGuiIO& io = ImGui::GetIO();
ImGuiKey key = keycodeToImGuiKey(keyCode);
if (key == ImGuiKey_None)
return;
if (!pressed && ImGui::IsKeyDown(key))
{
keysUpNextFrame[keyCode] = true;
return;
}
io.KeyCtrl = (modifiers & (0x01 | 0x10)) != 0;
io.KeyShift = (modifiers & (0x02 | 0x20)) != 0;
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(key, pressed);
}
@ -351,9 +357,9 @@ static void gui_newFrame()
ImGuiIO& io = ImGui::GetIO();
if (mouseX < 0 || mouseX >= settings.display.width || mouseY < 0 || mouseY >= settings.display.height)
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
else
io.MousePos = ImVec2(mouseX, mouseY);
io.AddMousePosEvent(mouseX, mouseY);
static bool delayTouch;
#if defined(__ANDROID__) || defined(TARGET_IPHONE)
// Delay touch by one frame to allow widgets to be hovered before click
@ -365,14 +371,14 @@ static void gui_newFrame()
#endif
if (io.WantCaptureMouse)
{
io.MouseWheel = -mouseWheel / 16;
io.AddMouseWheelEvent(0, -mouseWheel / 16);
mouseWheel = 0;
}
if (!delayTouch)
io.MouseDown[ImGuiMouseButton_Left] = (mouseButtons & (1 << 0)) != 0;
io.MouseDown[ImGuiMouseButton_Right] = (mouseButtons & (1 << 1)) != 0;
io.MouseDown[ImGuiMouseButton_Middle] = (mouseButtons & (1 << 2)) != 0;
io.MouseDown[3] = (mouseButtons & (1 << 3)) != 0;
io.AddMouseButtonEvent(ImGuiMouseButton_Left, (mouseButtons & (1 << 0)) != 0);
io.AddMouseButtonEvent(ImGuiMouseButton_Right, (mouseButtons & (1 << 1)) != 0);
io.AddMouseButtonEvent(ImGuiMouseButton_Middle, (mouseButtons & (1 << 2)) != 0);
io.AddMouseButtonEvent(3, (mouseButtons & (1 << 3)) != 0);
io.AddKeyEvent(ImGuiKey_GamepadFaceLeft, ((kcode[0] & DC_BTN_X) == 0));
io.AddKeyEvent(ImGuiKey_GamepadFaceRight, ((kcode[0] & DC_BTN_B) == 0));
@ -2496,7 +2502,7 @@ static void gui_display_content()
scanner.fetch_game_list();
// Only if Filter and Settings aren't focused... ImGui::SetNextWindowFocus();
ImGui::BeginChild(ImGui::GetID("library"), ImVec2(0, 0), true, ImGuiWindowFlags_DragScrolling);
ImGui::BeginChild(ImGui::GetID("library"), ImVec2(0, 0), true, ImGuiWindowFlags_DragScrolling | ImGuiWindowFlags_NavFlattened);
{
const int itemsPerLine = std::max<int>(ImGui::GetContentRegionMax().x / (150 * settings.display.uiScale + ImGui::GetStyle().ItemSpacing.x), 1);
const float responsiveBoxSize = ImGui::GetContentRegionMax().x / itemsPerLine - ImGui::GetStyle().FramePadding.x * 2;

View File

@ -35,7 +35,7 @@ void gui_refresh_files();
void gui_cheats();
void gui_keyboard_input(u16 wc);
void gui_keyboard_inputUTF8(const std::string& s);
void gui_keyboard_key(u8 keyCode, bool pressed, u8 modifiers);
void gui_keyboard_key(u8 keyCode, bool pressed);
bool gui_keyboard_captured();
bool gui_mouse_captured();
void gui_set_mouse_position(int x, int y);

View File

@ -359,8 +359,16 @@ void scrollWhenDraggingOnVoid(ImGuiMouseButton mouse_button)
bool held = false;
ImGuiButtonFlags button_flags = (mouse_button == ImGuiMouseButton_Left) ? ImGuiButtonFlags_MouseButtonLeft
: (mouse_button == ImGuiMouseButton_Right) ? ImGuiButtonFlags_MouseButtonRight : ImGuiButtonFlags_MouseButtonMiddle;
if (g.HoveredId == 0) // If nothing hovered so far in the frame (not same as IsAnyItemHovered()!)
ImGui::ButtonBehavior(window->Rect(), window->GetID("##scrolldraggingoverlay"), &hovered, &held, button_flags);
// If nothing hovered so far in the frame (not same as IsAnyItemHovered()!) or item is disabled
if (g.HoveredId == 0 || g.HoveredIdDisabled)
{
bool hoveredAllowOverlap = g.HoveredIdAllowOverlap;
g.HoveredIdAllowOverlap = true;
ImGuiID overlayId = window->GetID("##scrolldraggingoverlay");
ImGui::ButtonBehavior(window->Rect(), overlayId, &hovered, &held, button_flags);
ImGui::KeepAliveID(overlayId);
g.HoveredIdAllowOverlap = hoveredAllowOverlap;
}
const ImVec2& delta = ImGui::GetIO().MouseDelta;
if (held && delta != ImVec2())
{
@ -654,14 +662,14 @@ bool OptionCheckbox(const char *name, config::Option<bool, PerGameOption>& optio
template bool OptionCheckbox(const char *name, config::Option<bool, true>& option, const char *help);
template bool OptionCheckbox(const char *name, config::Option<bool, false>& option, const char *help);
bool OptionSlider(const char *name, config::Option<int>& option, int min, int max, const char *help)
bool OptionSlider(const char *name, config::Option<int>& option, int min, int max, const char *help, const char *format)
{
bool valueChanged;
{
DisabledScope scope(option.isReadOnly());
int v = option;
valueChanged = ImGui::SliderInt(name, &v, min, max);
valueChanged = ImGui::SliderInt(name, &v, min, max, format);
if (valueChanged)
option.set(v);
}

View File

@ -44,7 +44,7 @@ IMGUI_API const ImWchar* GetGlyphRangesChineseTraditionalOfficial();// Defaul
void ShowHelpMarker(const char* desc);
template<bool PerGameOption>
bool OptionCheckbox(const char *name, config::Option<bool, PerGameOption>& option, const char *help = nullptr);
bool OptionSlider(const char *name, config::Option<int>& option, int min, int max, const char *help = nullptr);
bool OptionSlider(const char *name, config::Option<int>& option, int min, int max, const char *help = nullptr, const char *format = nullptr);
template<typename T>
bool OptionRadioButton(const char *name, config::Option<T>& option, T value, const char *help = nullptr);
void OptionComboBox(const char *name, config::Option<int>& option, const char *values[], int count,

View File

@ -652,8 +652,8 @@ bool checkTryDebug()
changeText = nil;
if (textField.markedTextRange == nil) {
// it wants to replace text with nothing, ie a delete
gui_keyboard_key(0x2A, true, 0); // backspace
gui_keyboard_key(0x2A, false, 0);
gui_keyboard_key(0x2A, true); // backspace
gui_keyboard_key(0x2A, false);
}
if (textField.text.length < 16) {
textField.text = obligateForBackspace;
@ -667,8 +667,8 @@ bool checkTryDebug()
// Terminates the editing session
- (BOOL)textFieldShouldReturn:(UITextField*)_textField
{
gui_keyboard_key(0x28, true, 0); // Return
gui_keyboard_key(0x28, false, 0);
gui_keyboard_key(0x28, true); // Return
gui_keyboard_key(0x28, false);
return YES;
}