Merge branch 'master' into docking + incl add wd->Pipeline in ImGui_ImplVulkan_RenderDrawData platform code (#3455, #3459)

This commit is contained in:
omar 2020-09-08 16:55:09 +02:00
commit 78f753ffff
13 changed files with 349 additions and 204 deletions

View File

@ -109,14 +109,21 @@ Other Changes:
- Window: Fixed using non-zero pivot in SetNextWindowPos() when the window is collapsed. (#3433) - 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 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) - 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 selection helpers in ImGuiInputTextCallbackData().
- InputText: Added ImGuiInputTextFlags_CallbackEdit to modify internally owned buffer after an edit. - 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 (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the
underlying buffer while focus is active). underlying buffer while focus is active).
- InputText: Fixed using ImGuiInputTextFlags_Password with InputTextMultiline(). (#3427, #3428) - 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! 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 - DragFloat, DragScalar: Fixed ImGuiSliderFlags_ClampOnInput not being honored in the special case
where v_min == v_max. (#3361) 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(), - 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. 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 - 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] tabs reordered in the tab list popup. [@Xipiryon]
- Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. - 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'). - 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: 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]
----------------------------------------------------------------------- -----------------------------------------------------------------------

View File

@ -28,9 +28,11 @@ set(IMGUI_DIR ../../)
include_directories(${IMGUI_DIR} ..) include_directories(${IMGUI_DIR} ..)
# Libraries # Libraries
find_library(VULKAN_LIBRARY find_package(Vulkan REQUIRED)
NAMES vulkan vulkan-1) #find_library(VULKAN_LIBRARY
set(LIBRARIES "glfw;${VULKAN_LIBRARY}") #NAMES vulkan vulkan-1)
#set(LIBRARIES "glfw;${VULKAN_LIBRARY}")
set(LIBRARIES "glfw;Vulkan::Vulkan")
# Use vulkan headers from glfw: # Use vulkan headers from glfw:
include_directories(${GLFW_DIR}/deps) include_directories(${GLFW_DIR}/deps)

View File

@ -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.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.enabledExtensionCount = extensions_count; create_info.enabledExtensionCount = extensions_count;
create_info.ppEnabledExtensionNames = extensions; create_info.ppEnabledExtensionNames = extensions;
#ifdef IMGUI_VULKAN_DEBUG_REPORT #ifdef IMGUI_VULKAN_DEBUG_REPORT
// Enabling multiple validation layers grouped as LunarG standard validation // Enabling validation layers
const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; const char* layers[] = { "VK_LAYER_KHRONOS_validation" };
create_info.enabledLayerCount = 1; create_info.enabledLayerCount = 1;
create_info.ppEnabledLayerNames = layers; create_info.ppEnabledLayerNames = layers;

View File

@ -99,6 +99,8 @@ int main(int, char**)
ImGui_ImplSDL2_ProcessEvent(&event); ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
done = true; 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 // Start the Dear ImGui frame

View File

@ -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.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.enabledExtensionCount = extensions_count; create_info.enabledExtensionCount = extensions_count;
create_info.ppEnabledExtensionNames = extensions; create_info.ppEnabledExtensionNames = extensions;
#ifdef IMGUI_VULKAN_DEBUG_REPORT #ifdef IMGUI_VULKAN_DEBUG_REPORT
// Enabling multiple validation layers grouped as LunarG standard validation // Enabling validation layers
const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; const char* layers[] = { "VK_LAYER_KHRONOS_validation" };
create_info.enabledLayerCount = 1; create_info.enabledLayerCount = 1;
create_info.ppEnabledLayerNames = layers; create_info.ppEnabledLayerNames = layers;
@ -467,6 +466,8 @@ int main(int, char**)
ImGui_ImplSDL2_ProcessEvent(&event); ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
done = true; done = true;
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
done = true;
} }
// Resize swap chain? // Resize swap chain?

View File

@ -23,6 +23,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)
// 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-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. // 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. // 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 VkPipelineLayout g_PipelineLayout = VK_NULL_HANDLE;
static VkDescriptorSet g_DescriptorSet = VK_NULL_HANDLE; static VkDescriptorSet g_DescriptorSet = VK_NULL_HANDLE;
static VkPipeline g_Pipeline = VK_NULL_HANDLE; static VkPipeline g_Pipeline = VK_NULL_HANDLE;
static VkShaderModule g_ShaderModuleVert;
static VkShaderModule g_ShaderModuleFrag;
// Font data // Font data
static VkSampler g_FontSampler = VK_NULL_HANDLE; static VkSampler g_FontSampler = VK_NULL_HANDLE;
@ -281,11 +284,11 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory
p_buffer_size = new_size; 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: // 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 }; VkDescriptorSet desc_set[1] = { g_DescriptorSet };
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); 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 // 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) // (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) // 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); 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; return;
ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; 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. // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage.
ImGuiViewportDataVulkan* viewport_renderer_data = (ImGuiViewportDataVulkan*)draw_data->OwnerViewport->RendererUserData; 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 // 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 // Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports 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() // 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.) // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) 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 else
pcmd->UserCallback(cmd_list, pcmd); pcmd->UserCallback(cmd_list, pcmd);
} }
@ -603,6 +608,195 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)
return true; 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() bool ImGui_ImplVulkan_CreateDeviceObjects()
{ {
ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo;
@ -688,105 +882,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
check_vk_result(err); check_vk_result(err);
} }
VkPipelineShaderStageCreateInfo stage[2] = {}; ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, g_RenderPass, v->MSAASamples, &g_Pipeline);
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);
vkDestroyShaderModule(v->Device, vert_module, v->Allocator); vkDestroyShaderModule(v->Device, vert_module, v->Allocator);
vkDestroyShaderModule(v->Device, frag_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; wd->ImageCount = 0;
if (wd->RenderPass) if (wd->RenderPass)
vkDestroyRenderPass(device, wd->RenderPass, allocator); 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 was not specified, request different count of images dependent on selected present mode
if (min_image_count == 0) if (min_image_count == 0)
@ -1149,6 +1247,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
info.pDependencies = &dependency; info.pDependencies = &dependency;
err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass);
check_vk_result(err); 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 // 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); vkCmdEndRenderPass(fd->CommandBuffer);

View File

@ -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 bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass);
IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown();
IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); 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 bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer);
IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); 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) 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; VkSurfaceFormatKHR SurfaceFormat;
VkPresentModeKHR PresentMode; VkPresentModeKHR PresentMode;
VkRenderPass RenderPass; VkRenderPass RenderPass;
VkPipeline Pipeline; // The window pipeline uses a different VkRenderPass than the user's
bool ClearEnable; bool ClearEnable;
VkClearValue ClearValue; VkClearValue ClearValue;
uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount)

118
imgui.cpp
View File

@ -8083,21 +8083,42 @@ void ImGui::EndGroup()
// [SECTION] SCROLLING // [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) static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
{ {
ImVec2 scroll = window->Scroll; ImVec2 scroll = window->Scroll;
if (window->ScrollTarget.x < FLT_MAX) if (window->ScrollTarget.x < FLT_MAX)
{ {
float cr_x = window->ScrollTargetCenterRatio.x; float center_x_ratio = window->ScrollTargetCenterRatio.x;
float target_x = window->ScrollTarget.x; float scroll_target_x = window->ScrollTarget.x;
scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.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) if (window->ScrollTarget.y < FLT_MAX)
{ {
float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
float cr_y = window->ScrollTargetCenterRatio.y; float center_y_ratio = window->ScrollTargetCenterRatio.y;
float target_y = window->ScrollTarget.y; float scroll_target_y = window->ScrollTarget.y;
scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); 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.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
@ -8163,47 +8184,57 @@ float ImGui::GetScrollMaxY()
return window->ScrollMax.y; 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->ScrollTarget.x = scroll_x;
window->ScrollTargetCenterRatio.x = 0.0f; 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) void ImGui::SetScrollY(float scroll_y)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiContext& g = *GImGui;
window->ScrollTarget.y = scroll_y; SetScrollY(g.CurrentWindow, scroll_y);
window->ScrollTargetCenterRatio.y = 0.0f;
} }
void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x) // Note that a local position will vary depending on initial scroll value,
{ // This is a little bit confusing so bear with us:
window->ScrollTarget.x = new_scroll_x; // - local_pos = (absolution_pos - window->Pos)
window->ScrollTargetCenterRatio.x = 0.0f; // - 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.
void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y) // Following the rules above, when trying to work with scrolling code, consider that:
{ // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
window->ScrollTarget.y = new_scroll_y; // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
window->ScrollTargetCenterRatio.y = 0.0f; // 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
}
// 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
void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
{ {
IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); 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->ScrollTargetCenterRatio.x = center_x_ratio;
window->ScrollTargetEdgeSnapDist.x = 0.0f;
} }
void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
{ {
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); 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 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->ScrollTargetCenterRatio.y = center_y_ratio;
window->ScrollTargetEdgeSnapDist.y = 0.0f;
} }
void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) 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); 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. // 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) void ImGui::SetScrollHereX(float center_x_ratio)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
float spacing_x = g.Style.ItemSpacing.x; 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 // 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); window->ScrollTargetEdgeSnapDist.x = 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);
} }
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. // 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; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
float spacing_y = g.Style.ItemSpacing.y; 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 // 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); window->ScrollTargetEdgeSnapDist.y = 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);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -1470,7 +1470,7 @@ struct ImVector
inline bool empty() const { return Size == 0; } inline bool empty() const { return Size == 0; }
inline int size() const { return Size; } inline int size() const { return Size; }
inline int size_in_bytes() const { return Size * (int)sizeof(T); } 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 int capacity() const { return Capacity; }
inline T& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } 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]; } 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 BuildLookupTable();
IMGUI_API void ClearOutputData(); IMGUI_API void ClearOutputData();
IMGUI_API void GrowIndex(int new_size); 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 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 SetGlyphVisible(ImWchar c, bool visible);
IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API void SetFallbackChar(ImWchar c);

View File

@ -672,7 +672,7 @@ static void ShowDemoWindowWidgets()
"Hold SHIFT/ALT for faster/slower edit.\n" "Hold SHIFT/ALT for faster/slower edit.\n"
"Double-click or CTRL+click to input value."); "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; static float f1 = 1.00f, f2 = 0.0067f;
ImGui::DragFloat("drag float", &f1, 0.005f); 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 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::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; static bool inputs_step = true;
ImGui::Text("Inputs"); ImGui::Text("Inputs");
ImGui::Checkbox("Show step buttons", &inputs_step); ImGui::Checkbox("Show step buttons", &inputs_step);
@ -4291,9 +4299,9 @@ struct ExampleAppConsole
} }
ImGui::TextWrapped( 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."); "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 // TODO: display items starting from the bottom

View File

@ -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. // 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). // 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. // '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) if (cfg != NULL)
{ {

View File

@ -1774,6 +1774,7 @@ struct IMGUI_API ImGuiWindow
ImVec2 ScrollMax; 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 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 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. 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 ScrollbarX, ScrollbarY; // Are scrollbars visible?
bool ViewportOwned; bool ViewportOwned;
@ -2043,10 +2044,10 @@ namespace ImGui
// Scrolling // Scrolling
IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // Use -1.0f on one axis to leave as-is 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 SetScrollX(ImGuiWindow* window, float scroll_x);
IMGUI_API void SetScrollY(ImGuiWindow* window, float new_scroll_y); IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y);
IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio = 0.5f); 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 = 0.5f); IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio);
IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect); IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect);
// Basic Accessors // Basic Accessors
@ -2262,8 +2263,8 @@ namespace ImGui
// Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // 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). // 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<float, float>(const char* format, ImGuiDataType data_type, float v); " // e.g. " extern template IMGUI_API float RoundScalarWithFormatT<float, float>(const char* format, ImGuiDataType data_type, float v); "
template<typename T, typename FLOAT_T> 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<typename T, typename SIGNED_T, typename FLOAT_T> 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<typename T, typename FLOAT_T> 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<typename T, typename SIGNED_T, typename FLOAT_T> 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<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags); template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);
template<typename T, typename SIGNED_T, typename FLOAT_T> 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<typename T, typename SIGNED_T, typename FLOAT_T> 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<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);

View File

@ -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 // Clamp, both sides are optional, return true if modified
if (v_min && *v < *v_min) { *v = *v_min; return true; } 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; 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); logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);
// Convert to parametric space, apply delta, convert back // Convert to parametric space, apply delta, convert back
float v_old_parametric = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); float v_old_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(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; float v_new_parametric = v_old_parametric + g.DragCurrentAccum;
v_cur = ScaleValueFromRatioT<TYPE, FLOATTYPE>(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); v_cur = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(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; v_old_ref_for_accum_remainder = v_old_parametric;
} }
else else
@ -2120,7 +2120,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
if (is_logarithmic) if (is_logarithmic)
{ {
// Convert to parametric space, apply delta, convert back // Convert to parametric space, apply delta, convert back
float v_new_parametric = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); float v_new_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(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); g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder);
} }
else 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) // 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<typename TYPE, typename FLOATTYPE> template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
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) 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) if (v_min == v_max)
@ -2508,11 +2508,11 @@ float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, T
} }
// Linear slider // 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) // Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT)
template<typename TYPE, typename FLOATTYPE> template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
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) 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) if (v_min == v_max)
@ -2571,15 +2571,19 @@ TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, T
} }
else else
{ {
// For integer values we want the clicking position to match the grab box so we round above // - 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.. // 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; // - 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
TYPE v_new_off_floor = (TYPE)(v_new_off_f); // range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits.
TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5); if (t < 1.0)
if (v_new_off_floor < v_new_off_round) {
result = v_min + v_new_off_round; 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 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) else if (g.SliderCurrentAccumDirty)
{ {
clicked_t = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(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 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); 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 // 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<TYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
if (!(flags & ImGuiSliderFlags_NoRoundToFormat)) if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new); v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
float new_clicked_t = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); float new_clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
if (delta > 0) if (delta > 0)
g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta); 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) if (set_new_value)
{ {
TYPE v_new = ScaleValueFromRatioT<TYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(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 // Round to user desired precision based on format string
if (!(flags & ImGuiSliderFlags_NoRoundToFormat)) if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
@ -2732,7 +2736,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
else else
{ {
// Output grab position so it can be displayed by the caller // Output grab position so it can be displayed by the caller
float grab_t = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
if (axis == ImGuiAxis_Y) if (axis == ImGuiAxis_Y)
grab_t = 1.0f - grab_t; grab_t = 1.0f - grab_t;
const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, 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 // Apply new value (or operations) then clamp
DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL); DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL);
if (p_clamp_min || p_clamp_max) 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); DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max);
}
// Only mark as edited if new value is different // Only mark as edited if new value is different
value_changed = memcmp(&data_backup, p_data, data_type_size) != 0; 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++ = c;
*dst = '\0'; *dst = '\0';
if (CursorPos + bytes_count >= pos) if (CursorPos >= pos + bytes_count)
CursorPos -= bytes_count; CursorPos -= bytes_count;
else if (CursorPos >= pos) else if (CursorPos >= pos)
CursorPos = pos; CursorPos = pos;
@ -4432,11 +4440,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Vertical scroll // Vertical scroll
if (is_multiline) if (is_multiline)
{ {
// Test if cursor is vertically visible
float scroll_y = draw_window->Scroll.y; 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) if (cursor_offset.y - g.FontSize < scroll_y)
scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
else if (cursor_offset.y - inner_size.y >= scroll_y) 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_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
draw_window->Scroll.y = scroll_y; 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) if (is_multiline)
{ {
Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line Dummy(text_size);
EndChild(); EndChild();
EndGroup(); EndGroup();
} }