diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 82491f67..76849efc 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -109,14 +109,21 @@ Other Changes: - Window: Fixed using non-zero pivot in SetNextWindowPos() when the window is collapsed. (#3433) - Nav: Fixed navigation resuming on first visible item when using gamepad. [@rokups] - Nav: Fixed using Alt to toggle the Menu layer when inside a Modal window. (#787) +- Scrolling: Fixed SetScrollHere functions edge snapping when called during a frame where ContentSize + is changing (issue introduced in 1.78). (#3452). - InputText: Added selection helpers in ImGuiInputTextCallbackData(). - InputText: Added ImGuiInputTextFlags_CallbackEdit to modify internally owned buffer after an edit. (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active). - InputText: Fixed using ImGuiInputTextFlags_Password with InputTextMultiline(). (#3427, #3428) It is a rather unusual or useless combination of features but no reason it shouldn't work! +- InputText: Fixed minor scrolling glitch when erasing trailing lines in InputTextMultiline(). +- InputText: Fixed cursor being partially covered after using Ctrl+End key. +- InputText: Fixed callback's helper DeleteChars() function when cursor is inside the deleted block. (#3454). - DragFloat, DragScalar: Fixed ImGuiSliderFlags_ClampOnInput not being honored in the special case where v_min == v_max. (#3361) +- SliderInt, SliderScalar: Fixed reaching of maximum value with inverted integer min/max ranges, both + with signed and unsigned types. Added reverse Sliders to Demo. (#3432, #3449) [@rokups] - BeginMenuBar: Fixed minor bug where CursorPosMax gets pushed to CursorPos prior to calling BeginMenuBar(), so e.g. calling the function at the end of a window would often add +ItemSpacing.y to scrolling range. - TreeNode, CollapsingHeader: Made clicking on arrow toggle toggle the open state on the Mouse Down event @@ -128,7 +135,11 @@ Other Changes: tabs reordered in the tab list popup. [@Xipiryon] - Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. - Demo: Add simple InputText() callbacks demo (aside from the more elaborate ones in 'Examples->Console'). +- Backends: Vulkan: Some internal refactor aimed at allowing multi-viewport feature to create their + own render pass. (#3455, #3459) [@FunMiles] - Examples: Vulkan: Reworked buffer resize handling, fix for Linux/X11. (#3390, #2626) [@RoryO] +- Examples: Vulkan: Switch validation layer to use "VK_LAYER_KHRONOS_validation" instead of + "VK_LAYER_LUNARG_standard_validation" which is deprecated (#3459) [@FunMiles] ----------------------------------------------------------------------- diff --git a/examples/example_glfw_vulkan/CMakeLists.txt b/examples/example_glfw_vulkan/CMakeLists.txt index 68155ec8..023d791b 100644 --- a/examples/example_glfw_vulkan/CMakeLists.txt +++ b/examples/example_glfw_vulkan/CMakeLists.txt @@ -28,9 +28,11 @@ set(IMGUI_DIR ../../) include_directories(${IMGUI_DIR} ..) # Libraries -find_library(VULKAN_LIBRARY - NAMES vulkan vulkan-1) -set(LIBRARIES "glfw;${VULKAN_LIBRARY}") +find_package(Vulkan REQUIRED) +#find_library(VULKAN_LIBRARY + #NAMES vulkan vulkan-1) +#set(LIBRARIES "glfw;${VULKAN_LIBRARY}") +set(LIBRARIES "glfw;Vulkan::Vulkan") # Use vulkan headers from glfw: include_directories(${GLFW_DIR}/deps) diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 7c83a07d..08307be6 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -72,10 +72,9 @@ static void SetupVulkan(const char** extensions, uint32_t extensions_count) create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.enabledExtensionCount = extensions_count; create_info.ppEnabledExtensionNames = extensions; - #ifdef IMGUI_VULKAN_DEBUG_REPORT - // Enabling multiple validation layers grouped as LunarG standard validation - const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; + // Enabling validation layers + const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; create_info.enabledLayerCount = 1; create_info.ppEnabledLayerNames = layers; diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index 12c389a7..61b1dc9e 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -99,6 +99,8 @@ int main(int, char**) ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + done = true; } // Start the Dear ImGui frame diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index 88195644..9b8ac52f 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -64,10 +64,9 @@ static void SetupVulkan(const char** extensions, uint32_t extensions_count) create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.enabledExtensionCount = extensions_count; create_info.ppEnabledExtensionNames = extensions; - #ifdef IMGUI_VULKAN_DEBUG_REPORT - // Enabling multiple validation layers grouped as LunarG standard validation - const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; + // Enabling validation layers + const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; create_info.enabledLayerCount = 1; create_info.ppEnabledLayerNames = layers; @@ -467,6 +466,8 @@ int main(int, char**) ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + done = true; } // Resize swap chain? diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 2b8af9d2..52238ffb 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). // 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. // 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices. // 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before. @@ -93,6 +94,8 @@ static VkDescriptorSetLayout g_DescriptorSetLayout = VK_NULL_HANDLE; static VkPipelineLayout g_PipelineLayout = VK_NULL_HANDLE; static VkDescriptorSet g_DescriptorSet = VK_NULL_HANDLE; static VkPipeline g_Pipeline = VK_NULL_HANDLE; +static VkShaderModule g_ShaderModuleVert; +static VkShaderModule g_ShaderModuleFrag; // Font data static VkSampler g_FontSampler = VK_NULL_HANDLE; @@ -281,11 +284,11 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory p_buffer_size = new_size; } -static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) +static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) { // Bind pipeline and descriptor sets: { - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_Pipeline); + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); VkDescriptorSet desc_set[1] = { g_DescriptorSet }; vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); } @@ -327,7 +330,7 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkCommandBu // Render function // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer) +void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); @@ -336,6 +339,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm return; ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + if (pipeline == VK_NULL_HANDLE) + pipeline = g_Pipeline; // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. ImGuiViewportDataVulkan* viewport_renderer_data = (ImGuiViewportDataVulkan*)draw_data->OwnerViewport->RendererUserData; @@ -391,7 +396,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm } // Setup desired Vulkan state - ImGui_ImplVulkan_SetupRenderState(draw_data, command_buffer, rb, fb_width, fb_height); + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports @@ -412,7 +417,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplVulkan_SetupRenderState(draw_data, command_buffer, rb, fb_width, fb_height); + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); else pcmd->UserCallback(cmd_list, pcmd); } @@ -603,6 +608,195 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) return true; } +static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) +{ + // Create the shader modules + if (g_ShaderModuleVert == NULL) + { + VkShaderModuleCreateInfo vert_info = {}; + vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vert_info.codeSize = sizeof(__glsl_shader_vert_spv); + vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; + VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &g_ShaderModuleVert); + check_vk_result(err); + } + if (g_ShaderModuleFrag == NULL) + { + VkShaderModuleCreateInfo frag_info = {}; + frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + frag_info.codeSize = sizeof(__glsl_shader_frag_spv); + frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; + VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &g_ShaderModuleFrag); + check_vk_result(err); + } +} + +static void ImGui_ImplVulkan_CreateFontSampler(VkDevice device, const VkAllocationCallbacks* allocator) +{ + if (g_FontSampler) + return; + + VkSamplerCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.minLod = -1000; + info.maxLod = 1000; + info.maxAnisotropy = 1.0f; + VkResult err = vkCreateSampler(device, &info, allocator, &g_FontSampler); + check_vk_result(err); +} + +static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device, const VkAllocationCallbacks* allocator) +{ + if (g_DescriptorSetLayout) + return; + + ImGui_ImplVulkan_CreateFontSampler(device, allocator); + VkSampler sampler[1] = { g_FontSampler }; + VkDescriptorSetLayoutBinding binding[1] = {}; + binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding[0].descriptorCount = 1; + binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + binding[0].pImmutableSamplers = sampler; + VkDescriptorSetLayoutCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.bindingCount = 1; + info.pBindings = binding; + VkResult err = vkCreateDescriptorSetLayout(device, &info, allocator, &g_DescriptorSetLayout); + check_vk_result(err); +} + +static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAllocationCallbacks* allocator) +{ + if (g_PipelineLayout) + return; + + // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix + ImGui_ImplVulkan_CreateDescriptorSetLayout(device, allocator); + VkPushConstantRange push_constants[1] = {}; + push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constants[0].offset = sizeof(float) * 0; + push_constants[0].size = sizeof(float) * 4; + VkDescriptorSetLayout set_layout[1] = { g_DescriptorSetLayout }; + VkPipelineLayoutCreateInfo layout_info = {}; + layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_info.setLayoutCount = 1; + layout_info.pSetLayouts = set_layout; + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = push_constants; + VkResult err = vkCreatePipelineLayout(device, &layout_info, allocator, &g_PipelineLayout); + check_vk_result(err); +} + +static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline *pipeline) +{ + ImGui_ImplVulkan_CreateShaderModules(device, allocator); + + VkPipelineShaderStageCreateInfo stage[2] = {}; + stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stage[0].module = g_ShaderModuleVert; + stage[0].pName = "main"; + stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stage[1].module = g_ShaderModuleFrag; + stage[1].pName = "main"; + + VkVertexInputBindingDescription binding_desc[1] = {}; + binding_desc[0].stride = sizeof(ImDrawVert); + binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attribute_desc[3] = {}; + attribute_desc[0].location = 0; + attribute_desc[0].binding = binding_desc[0].binding; + attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos); + attribute_desc[1].location = 1; + attribute_desc[1].binding = binding_desc[0].binding; + attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv); + attribute_desc[2].location = 2; + attribute_desc[2].binding = binding_desc[0].binding; + attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; + attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col); + + VkPipelineVertexInputStateCreateInfo vertex_info = {}; + vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_info.vertexBindingDescriptionCount = 1; + vertex_info.pVertexBindingDescriptions = binding_desc; + vertex_info.vertexAttributeDescriptionCount = 3; + vertex_info.pVertexAttributeDescriptions = attribute_desc; + + VkPipelineInputAssemblyStateCreateInfo ia_info = {}; + ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo viewport_info = {}; + viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_info.viewportCount = 1; + viewport_info.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo raster_info = {}; + raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + raster_info.polygonMode = VK_POLYGON_MODE_FILL; + raster_info.cullMode = VK_CULL_MODE_NONE; + raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + raster_info.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo ms_info = {}; + ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms_info.rasterizationSamples = (MSAASamples != 0) ? MSAASamples : VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState color_attachment[1] = {}; + color_attachment[0].blendEnable = VK_TRUE; + color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineDepthStencilStateCreateInfo depth_info = {}; + depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + VkPipelineColorBlendStateCreateInfo blend_info = {}; + blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + blend_info.attachmentCount = 1; + blend_info.pAttachments = color_attachment; + + VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamic_state = {}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); + dynamic_state.pDynamicStates = dynamic_states; + + ImGui_ImplVulkan_CreatePipelineLayout(device, allocator); + + VkGraphicsPipelineCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + info.flags = g_PipelineCreateFlags; + info.stageCount = 2; + info.pStages = stage; + info.pVertexInputState = &vertex_info; + info.pInputAssemblyState = &ia_info; + info.pViewportState = &viewport_info; + info.pRasterizationState = &raster_info; + info.pMultisampleState = &ms_info; + info.pDepthStencilState = &depth_info; + info.pColorBlendState = &blend_info; + info.pDynamicState = &dynamic_state; + info.layout = g_PipelineLayout; + info.renderPass = renderPass; + VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + check_vk_result(err); +} + bool ImGui_ImplVulkan_CreateDeviceObjects() { ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; @@ -688,105 +882,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - VkPipelineShaderStageCreateInfo stage[2] = {}; - stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - stage[0].module = vert_module; - stage[0].pName = "main"; - stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - stage[1].module = frag_module; - stage[1].pName = "main"; - - VkVertexInputBindingDescription binding_desc[1] = {}; - binding_desc[0].stride = sizeof(ImDrawVert); - binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - VkVertexInputAttributeDescription attribute_desc[3] = {}; - attribute_desc[0].location = 0; - attribute_desc[0].binding = binding_desc[0].binding; - attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; - attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos); - attribute_desc[1].location = 1; - attribute_desc[1].binding = binding_desc[0].binding; - attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; - attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv); - attribute_desc[2].location = 2; - attribute_desc[2].binding = binding_desc[0].binding; - attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; - attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col); - - VkPipelineVertexInputStateCreateInfo vertex_info = {}; - vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertex_info.vertexBindingDescriptionCount = 1; - vertex_info.pVertexBindingDescriptions = binding_desc; - vertex_info.vertexAttributeDescriptionCount = 3; - vertex_info.pVertexAttributeDescriptions = attribute_desc; - - VkPipelineInputAssemblyStateCreateInfo ia_info = {}; - ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - VkPipelineViewportStateCreateInfo viewport_info = {}; - viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewport_info.viewportCount = 1; - viewport_info.scissorCount = 1; - - VkPipelineRasterizationStateCreateInfo raster_info = {}; - raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - raster_info.polygonMode = VK_POLYGON_MODE_FILL; - raster_info.cullMode = VK_CULL_MODE_NONE; - raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - raster_info.lineWidth = 1.0f; - - VkPipelineMultisampleStateCreateInfo ms_info = {}; - ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - if (v->MSAASamples != 0) - ms_info.rasterizationSamples = v->MSAASamples; - else - ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState color_attachment[1] = {}; - color_attachment[0].blendEnable = VK_TRUE; - color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; - color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; - color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - - VkPipelineDepthStencilStateCreateInfo depth_info = {}; - depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - - VkPipelineColorBlendStateCreateInfo blend_info = {}; - blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - blend_info.attachmentCount = 1; - blend_info.pAttachments = color_attachment; - - VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineDynamicStateCreateInfo dynamic_state = {}; - dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); - dynamic_state.pDynamicStates = dynamic_states; - - VkGraphicsPipelineCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - info.flags = g_PipelineCreateFlags; - info.stageCount = 2; - info.pStages = stage; - info.pVertexInputState = &vertex_info; - info.pInputAssemblyState = &ia_info; - info.pViewportState = &viewport_info; - info.pRasterizationState = &raster_info; - info.pMultisampleState = &ms_info; - info.pDepthStencilState = &depth_info; - info.pColorBlendState = &blend_info; - info.pDynamicState = &dynamic_state; - info.layout = g_PipelineLayout; - info.renderPass = g_RenderPass; - err = vkCreateGraphicsPipelines(v->Device, v->PipelineCache, 1, &info, v->Allocator, &g_Pipeline); - check_vk_result(err); + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, g_RenderPass, v->MSAASamples, &g_Pipeline); vkDestroyShaderModule(v->Device, vert_module, v->Allocator); vkDestroyShaderModule(v->Device, frag_module, v->Allocator); @@ -1054,6 +1150,8 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V wd->ImageCount = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); + if (wd->Pipeline) + vkDestroyPipeline(device, wd->Pipeline, allocator); // If min image count was not specified, request different count of images dependent on selected present mode if (min_image_count == 0) @@ -1149,6 +1247,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V info.pDependencies = &dependency; err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); check_vk_result(err); + ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline); } // Create The Image Views @@ -1385,7 +1484,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) } } - ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer); + ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, wd->Pipeline); { vkCmdEndRenderPass(fd->CommandBuffer); diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index eb3229e1..f20a72f4 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -47,7 +47,7 @@ struct ImGui_ImplVulkan_InitInfo IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer); +IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) @@ -109,6 +109,7 @@ struct ImGui_ImplVulkanH_Window VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; VkRenderPass RenderPass; + VkPipeline Pipeline; // The window pipeline uses a different VkRenderPass than the user's bool ClearEnable; VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) diff --git a/imgui.cpp b/imgui.cpp index 07cc1897..cb26785d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8083,21 +8083,42 @@ void ImGui::EndGroup() // [SECTION] SCROLLING //----------------------------------------------------------------------------- +// Helper to snap on edges when aiming at an item very close to the edge, +// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling. +// When we refactor the scrolling API this may be configurable with a flag? +// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default. +static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio) +{ + if (target <= snap_min + snap_threshold) + return ImLerp(snap_min, target, center_ratio); + if (target >= snap_max - snap_threshold) + return ImLerp(target, snap_max, center_ratio); + return target; +} + static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) { ImVec2 scroll = window->Scroll; if (window->ScrollTarget.x < FLT_MAX) { - float cr_x = window->ScrollTargetCenterRatio.x; - float target_x = window->ScrollTarget.x; - scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x); + float center_x_ratio = window->ScrollTargetCenterRatio.x; + float scroll_target_x = window->ScrollTarget.x; + float snap_x_min = 0.0f; + float snap_x_max = window->ScrollMax.x + window->Size.x; + if (window->ScrollTargetEdgeSnapDist.x > 0.0f) + scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); + scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x); } if (window->ScrollTarget.y < FLT_MAX) { float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); - float cr_y = window->ScrollTargetCenterRatio.y; - float target_y = window->ScrollTarget.y; - scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); + float center_y_ratio = window->ScrollTargetCenterRatio.y; + float scroll_target_y = window->ScrollTarget.y; + float snap_y_min = 0.0f; + float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height; + if (window->ScrollTargetEdgeSnapDist.y > 0.0f) + scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); + scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); } scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); @@ -8163,47 +8184,57 @@ float ImGui::GetScrollMaxY() return window->ScrollMax.y; } -void ImGui::SetScrollX(float scroll_x) +void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x) { - ImGuiWindow* window = GetCurrentWindow(); window->ScrollTarget.x = scroll_x; window->ScrollTargetCenterRatio.x = 0.0f; + window->ScrollTargetEdgeSnapDist.x = 0.0f; +} + +void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y) +{ + window->ScrollTarget.y = scroll_y; + window->ScrollTargetCenterRatio.y = 0.0f; + window->ScrollTargetEdgeSnapDist.y = 0.0f; +} + +void ImGui::SetScrollX(float scroll_x) +{ + ImGuiContext& g = *GImGui; + SetScrollX(g.CurrentWindow, scroll_x); } void ImGui::SetScrollY(float scroll_y) { - ImGuiWindow* window = GetCurrentWindow(); - window->ScrollTarget.y = scroll_y; - window->ScrollTargetCenterRatio.y = 0.0f; + ImGuiContext& g = *GImGui; + SetScrollY(g.CurrentWindow, scroll_y); } -void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x) -{ - window->ScrollTarget.x = new_scroll_x; - window->ScrollTargetCenterRatio.x = 0.0f; -} - -void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y) -{ - window->ScrollTarget.y = new_scroll_y; - window->ScrollTargetCenterRatio.y = 0.0f; -} - -// Note that a local position will vary depending on initial scroll value -// We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size +// Note that a local position will vary depending on initial scroll value, +// This is a little bit confusing so bear with us: +// - local_pos = (absolution_pos - window->Pos) +// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window, +// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area. +// - They mostly exists because of legacy API. +// Following the rules above, when trying to work with scrolling code, consider that: +// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect! +// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense +// We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) { IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); - window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); + window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset window->ScrollTargetCenterRatio.x = center_x_ratio; + window->ScrollTargetEdgeSnapDist.x = 0.0f; } void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect - window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); + window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; + window->ScrollTargetEdgeSnapDist.y = 0.0f; } void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) @@ -8218,34 +8249,17 @@ void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio); } -// Tweak: snap on edges when aiming at an item very close to the edge, -// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling. -// When we refactor the scrolling API this may be configurable with a flag? -// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default. -static float CalcScrollSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio) -{ - if (target <= snap_min + snap_threshold) - return ImLerp(snap_min, target, center_ratio); - if (target >= snap_max - snap_threshold) - return ImLerp(target, snap_max, center_ratio); - return target; -} - // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item. void ImGui::SetScrollHereX(float center_x_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float spacing_x = g.Style.ItemSpacing.x; - float target_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); + float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); + SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos // Tweak: snap on edges when aiming at an item very close to the edge - const float snap_x_threshold = ImMax(0.0f, window->WindowPadding.x - spacing_x); - const float snap_x_min = window->DC.CursorStartPos.x - window->WindowPadding.x; - const float snap_x_max = window->DC.CursorStartPos.x + window->ContentSize.x + window->WindowPadding.x; - target_x = CalcScrollSnap(target_x, snap_x_min, snap_x_max, snap_x_threshold, center_x_ratio); - - SetScrollFromPosX(window, target_x - window->Pos.x, center_x_ratio); + window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x); } // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. @@ -8254,15 +8268,11 @@ void ImGui::SetScrollHereY(float center_y_ratio) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float spacing_y = g.Style.ItemSpacing.y; - float target_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); + float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); + SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos // Tweak: snap on edges when aiming at an item very close to the edge - const float snap_y_threshold = ImMax(0.0f, window->WindowPadding.y - spacing_y); - const float snap_y_min = window->DC.CursorStartPos.y - window->WindowPadding.y; - const float snap_y_max = window->DC.CursorStartPos.y + window->ContentSize.y + window->WindowPadding.y; - target_y = CalcScrollSnap(target_y, snap_y_min, snap_y_max, snap_y_threshold, center_y_ratio); - - SetScrollFromPosY(window, target_y - window->Pos.y, center_y_ratio); + window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y); } //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index ae5b40ca..14bb8741 100644 --- a/imgui.h +++ b/imgui.h @@ -1470,7 +1470,7 @@ struct ImVector inline bool empty() const { return Size == 0; } inline int size() const { return Size; } inline int size_in_bytes() const { return Size * (int)sizeof(T); } - inline int max_size() const { return (~(unsigned int)0) / (int)sizeof(T); } + inline int max_size() const { return 0x7FFFFFFF / (int)sizeof(T); } inline int capacity() const { return Capacity; } inline T& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } inline const T& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } @@ -2547,7 +2547,7 @@ struct ImFont IMGUI_API void BuildLookupTable(); IMGUI_API void ClearOutputData(); IMGUI_API void GrowIndex(int new_size); - IMGUI_API void AddGlyph(ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); + IMGUI_API void AddGlyph(const ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. IMGUI_API void SetGlyphVisible(ImWchar c, bool visible); IMGUI_API void SetFallbackChar(ImWchar c); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 4d9577e4..56d5c2c4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -672,7 +672,7 @@ static void ShowDemoWindowWidgets() "Hold SHIFT/ALT for faster/slower edit.\n" "Double-click or CTRL+click to input value."); - ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%"); + ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_ClampOnInput); static float f1 = 1.00f, f2 = 0.0067f; ImGui::DragFloat("drag float", &f1, 0.005f); @@ -1743,6 +1743,14 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", ImGuiSliderFlags_Logarithmic); ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams"); + ImGui::Text("Sliders (reverse)"); + ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d"); + ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u"); + ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d"); + ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u"); + ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%I64d"); + ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%I64u ms"); + static bool inputs_step = true; ImGui::Text("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); @@ -4291,9 +4299,9 @@ struct ExampleAppConsole } ImGui::TextWrapped( - "This example implements a console with basic coloring, completion and history. A more elaborate " + "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate " "implementation may want to store entries along with extra data such as timestamp, emitter, etc."); - ImGui::TextWrapped("Enter 'HELP' for help, press TAB to use text completion."); + ImGui::TextWrapped("Enter 'HELP' for help."); // TODO: display items starting from the bottom diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 81e42ffd..9e7454b2 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2892,7 +2892,7 @@ void ImFont::GrowIndex(int new_size) // x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. // Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis). // 'cfg' is not necessarily == 'this->ConfigData' because multiple source fonts+configs can be used to build one target font. -void ImFont::AddGlyph(ImFontConfig* cfg, ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x) +void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x) { if (cfg != NULL) { diff --git a/imgui_internal.h b/imgui_internal.h index d2549427..607ae378 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1774,6 +1774,7 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollMax; ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered + ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. bool ScrollbarX, ScrollbarY; // Are scrollbars visible? bool ViewportOwned; @@ -2043,10 +2044,10 @@ namespace ImGui // Scrolling IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // Use -1.0f on one axis to leave as-is - IMGUI_API void SetScrollX(ImGuiWindow* window, float new_scroll_x); - IMGUI_API void SetScrollY(ImGuiWindow* window, float new_scroll_y); - IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio = 0.5f); - IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio = 0.5f); + IMGUI_API void SetScrollX(ImGuiWindow* window, float scroll_x); + IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); + IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); + IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect); // Basic Accessors @@ -2262,8 +2263,8 @@ namespace ImGui // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " - template IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); - template IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); + template IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); + template IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags); template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2d516bc1..40e339a6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1949,7 +1949,7 @@ static bool DataTypeClampT(T* v, const T* v_min, const T* v_max) { // Clamp, both sides are optional, return true if modified if (v_min && *v < *v_min) { *v = *v_min; return true; } - if (v_max && *v > * v_max) { *v = *v_max; return true; } + if (v_max && *v > *v_max) { *v = *v_max; return true; } return false; } @@ -2101,9 +2101,9 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); // Convert to parametric space, apply delta, convert back - float v_old_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + float v_old_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); float v_new_parametric = v_old_parametric + g.DragCurrentAccum; - v_cur = ScaleValueFromRatioT(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + v_cur = ScaleValueFromRatioT(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); v_old_ref_for_accum_remainder = v_old_parametric; } else @@ -2120,7 +2120,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const if (is_logarithmic) { // Convert to parametric space, apply delta, convert back - float v_new_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + float v_new_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder); } else @@ -2456,7 +2456,7 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data //------------------------------------------------------------------------- // Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT) -template +template float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) { if (v_min == v_max) @@ -2508,11 +2508,11 @@ float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, T } // Linear slider - return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min)); + return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min)); } // Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT) -template +template TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) { if (v_min == v_max) @@ -2571,15 +2571,19 @@ TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, T } else { - // For integer values we want the clicking position to match the grab box so we round above - // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. - FLOATTYPE v_new_off_f = (v_max - v_min) * t; - TYPE v_new_off_floor = (TYPE)(v_new_off_f); - TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5); - if (v_new_off_floor < v_new_off_round) - result = v_min + v_new_off_round; + // - For integer values we want the clicking position to match the grab box so we round above + // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. + // - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 + // range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. + if (t < 1.0) + { + FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t; + result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5))); + } else - result = v_min + v_new_off_floor; + { + result = v_max; + } } } @@ -2679,7 +2683,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ } else if (g.SliderCurrentAccumDirty) { - clicked_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + clicked_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits { @@ -2693,10 +2697,10 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ clicked_t = ImSaturate(clicked_t + delta); // Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator - TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (!(flags & ImGuiSliderFlags_NoRoundToFormat)) v_new = RoundScalarWithFormatT(format, data_type, v_new); - float new_clicked_t = ScaleRatioFromValueT(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + float new_clicked_t = ScaleRatioFromValueT(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (delta > 0) g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta); @@ -2710,7 +2714,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ if (set_new_value) { - TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); // Round to user desired precision based on format string if (!(flags & ImGuiSliderFlags_NoRoundToFormat)) @@ -2732,7 +2736,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ else { // Output grab position so it can be displayed by the caller - float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (axis == ImGuiAxis_Y) grab_t = 1.0f - grab_t; const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); @@ -3194,7 +3198,11 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG // Apply new value (or operations) then clamp DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL); if (p_clamp_min || p_clamp_max) + { + if (DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) + ImSwap(p_clamp_min, p_clamp_max); DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max); + } // Only mark as edited if new value is different value_changed = memcmp(&data_backup, p_data, data_type_size) != 0; @@ -3627,7 +3635,7 @@ void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) *dst++ = c; *dst = '\0'; - if (CursorPos + bytes_count >= pos) + if (CursorPos >= pos + bytes_count) CursorPos -= bytes_count; else if (CursorPos >= pos) CursorPos = pos; @@ -4432,11 +4440,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Vertical scroll if (is_multiline) { + // Test if cursor is vertically visible float scroll_y = draw_window->Scroll.y; + const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); if (cursor_offset.y - g.FontSize < scroll_y) scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); else if (cursor_offset.y - inner_size.y >= scroll_y) - scroll_y = cursor_offset.y - inner_size.y; + scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; + scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag draw_window->Scroll.y = scroll_y; } @@ -4528,7 +4539,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline) { - Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line + Dummy(text_size); EndChild(); EndGroup(); }