|
@@ -2015,23 +2015,20 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void
|
|
|
ImGuiDataTypeTempStorage data_backup;
|
|
|
memcpy(&data_backup, p_data, type_info->Size);
|
|
|
|
|
|
- if (format == NULL)
|
|
|
+ // Sanitize format
|
|
|
+ // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
|
|
|
+ char format_sanitized[32];
|
|
|
+ if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
|
|
|
format = type_info->ScanFmt;
|
|
|
-
|
|
|
- if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64 || data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
|
|
|
- {
|
|
|
- // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
|
|
|
- if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
|
|
|
- format = type_info->ScanFmt;
|
|
|
- if (sscanf(buf, format, p_data) < 1)
|
|
|
- return false;
|
|
|
- }
|
|
|
else
|
|
|
+ format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized));
|
|
|
+
|
|
|
+ // Small types need a 32-bit buffer to receive the result from scanf()
|
|
|
+ int v32 = 0;
|
|
|
+ if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1)
|
|
|
+ return false;
|
|
|
+ if (type_info->Size < 4)
|
|
|
{
|
|
|
- // Small types need a 32-bit buffer to receive the result from scanf()
|
|
|
- int v32;
|
|
|
- if (sscanf(buf, format, &v32) < 1)
|
|
|
- return false;
|
|
|
if (data_type == ImGuiDataType_S8)
|
|
|
*(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX);
|
|
|
else if (data_type == ImGuiDataType_U8)
|
|
@@ -2113,45 +2110,17 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
|
|
|
}
|
|
|
|
|
|
template<typename TYPE>
|
|
|
-static const char* ImAtoi(const char* src, TYPE* output)
|
|
|
-{
|
|
|
- int negative = 0;
|
|
|
- if (*src == '-') { negative = 1; src++; }
|
|
|
- if (*src == '+') { src++; }
|
|
|
- TYPE v = 0;
|
|
|
- while (*src >= '0' && *src <= '9')
|
|
|
- v = (v * 10) + (*src++ - '0');
|
|
|
- *output = negative ? -v : v;
|
|
|
- return src;
|
|
|
-}
|
|
|
-
|
|
|
-// Sanitize format
|
|
|
-// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi
|
|
|
-// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
|
|
|
-static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_size)
|
|
|
-{
|
|
|
- IM_UNUSED(fmt_out_size);
|
|
|
- const char* fmt_end = ImParseFormatFindEnd(fmt);
|
|
|
- IM_ASSERT((size_t)(fmt_end - fmt + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
|
|
|
- while (fmt < fmt_end)
|
|
|
- {
|
|
|
- char c = *(fmt++);
|
|
|
- if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
|
|
|
- *(fmt_out++) = c;
|
|
|
- }
|
|
|
- *fmt_out = 0; // Zero-terminate
|
|
|
-}
|
|
|
-
|
|
|
-template<typename TYPE, typename SIGNEDTYPE>
|
|
|
TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)
|
|
|
{
|
|
|
+ IM_UNUSED(data_type);
|
|
|
+ IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
|
|
|
const char* fmt_start = ImParseFormatFindStart(format);
|
|
|
if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string
|
|
|
return v;
|
|
|
|
|
|
// Sanitize format
|
|
|
char fmt_sanitized[32];
|
|
|
- SanitizeFormatString(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized));
|
|
|
+ ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized));
|
|
|
fmt_start = fmt_sanitized;
|
|
|
|
|
|
// Format value with our rounding, and read back
|
|
@@ -2160,10 +2129,8 @@ TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type,
|
|
|
const char* p = v_str;
|
|
|
while (*p == ' ')
|
|
|
p++;
|
|
|
- if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
|
|
|
- v = (TYPE)ImAtof(p);
|
|
|
- else
|
|
|
- ImAtoi(p, (SIGNEDTYPE*)&v);
|
|
|
+ v = (TYPE)ImAtof(p);
|
|
|
+
|
|
|
return v;
|
|
|
}
|
|
|
|
|
@@ -2267,8 +2234,8 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
|
|
|
}
|
|
|
|
|
|
// Round to user desired precision based on format string
|
|
|
- if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
|
|
|
- v_cur = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
|
|
|
+ if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
|
|
|
+ v_cur = RoundScalarWithFormatT<TYPE>(format, data_type, v_cur);
|
|
|
|
|
|
// Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
|
|
|
g.DragCurrentAccumDirty = false;
|
|
@@ -2864,8 +2831,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
|
|
|
|
|
|
// 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, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
|
|
|
- if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
|
|
|
- v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
|
|
|
+ if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
|
|
|
+ v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
|
|
|
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)
|
|
@@ -2883,8 +2850,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
|
|
|
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
|
|
|
- if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
|
|
|
- v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
|
|
|
+ if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
|
|
|
+ v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
|
|
|
|
|
|
// Apply result
|
|
|
if (*v != v_new)
|
|
@@ -3227,6 +3194,8 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i
|
|
|
// - ImParseFormatFindStart() [Internal]
|
|
|
// - ImParseFormatFindEnd() [Internal]
|
|
|
// - ImParseFormatTrimDecorations() [Internal]
|
|
|
+// - ImParseFormatSanitizeForPrinting() [Internal]
|
|
|
+// - ImParseFormatSanitizeForScanning() [Internal]
|
|
|
// - ImParseFormatPrecision() [Internal]
|
|
|
// - TempInputTextScalar() [Internal]
|
|
|
// - InputScalar()
|
|
@@ -3290,6 +3259,57 @@ const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_
|
|
|
return buf;
|
|
|
}
|
|
|
|
|
|
+// Sanitize format
|
|
|
+// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi
|
|
|
+// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
|
|
|
+void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size)
|
|
|
+{
|
|
|
+ const char* fmt_end = ImParseFormatFindEnd(fmt_in);
|
|
|
+ IM_UNUSED(fmt_out_size);
|
|
|
+ IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
|
|
|
+ while (fmt_in < fmt_end)
|
|
|
+ {
|
|
|
+ char c = *fmt_in++;
|
|
|
+ if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
|
|
|
+ *(fmt_out++) = c;
|
|
|
+ }
|
|
|
+ *fmt_out = 0; // Zero-terminate
|
|
|
+}
|
|
|
+
|
|
|
+// - For scanning we need to remove all width and precision fields "%3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d"
|
|
|
+const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size)
|
|
|
+{
|
|
|
+ const char* fmt_end = ImParseFormatFindEnd(fmt_in);
|
|
|
+ const char* fmt_out_begin = fmt_out;
|
|
|
+ IM_UNUSED(fmt_out_size);
|
|
|
+ IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
|
|
|
+ bool has_type = false;
|
|
|
+ while (fmt_in < fmt_end)
|
|
|
+ {
|
|
|
+ char c = *fmt_in++;
|
|
|
+ if (!has_type && ((c >= '0' && c <= '9') || c == '.'))
|
|
|
+ continue;
|
|
|
+ has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits
|
|
|
+ if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
|
|
|
+ *(fmt_out++) = c;
|
|
|
+ }
|
|
|
+ *fmt_out = 0; // Zero-terminate
|
|
|
+ return fmt_out_begin;
|
|
|
+}
|
|
|
+
|
|
|
+template<typename TYPE>
|
|
|
+static const char* ImAtoi(const char* src, TYPE* output)
|
|
|
+{
|
|
|
+ int negative = 0;
|
|
|
+ if (*src == '-') { negative = 1; src++; }
|
|
|
+ if (*src == '+') { src++; }
|
|
|
+ TYPE v = 0;
|
|
|
+ while (*src >= '0' && *src <= '9')
|
|
|
+ v = (v * 10) + (*src++ - '0');
|
|
|
+ *output = negative ? -v : v;
|
|
|
+ return src;
|
|
|
+}
|
|
|
+
|
|
|
// Parse display precision back from the display format string
|
|
|
// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed.
|
|
|
int ImParseFormatPrecision(const char* fmt, int default_precision)
|
|
@@ -3336,6 +3356,14 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
|
|
|
return value_changed;
|
|
|
}
|
|
|
|
|
|
+static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(ImGuiDataType data_type, const char* format)
|
|
|
+{
|
|
|
+ if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
|
|
|
+ return ImGuiInputTextFlags_CharsScientific;
|
|
|
+ const char format_last_char = format[0] ? format[strlen(format) - 1] : 0;
|
|
|
+ return (format_last_char == 'x' || format_last_char == 'X') ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal;
|
|
|
+}
|
|
|
+
|
|
|
// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set!
|
|
|
// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility.
|
|
|
// However this may not be ideal for all uses, as some user code may break on out of bound values.
|
|
@@ -3348,7 +3376,8 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
|
|
|
ImStrTrimBlanks(data_buf);
|
|
|
|
|
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited;
|
|
|
- flags |= ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal);
|
|
|
+ flags |= InputScalar_DefaultCharsFilter(data_type, format);
|
|
|
+
|
|
|
bool value_changed = false;
|
|
|
if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))
|
|
|
{
|
|
@@ -3358,7 +3387,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
|
|
|
memcpy(&data_backup, p_data, data_type_size);
|
|
|
|
|
|
// Apply new value (or operations) then clamp
|
|
|
- DataTypeApplyFromText(data_buf, data_type, p_data, NULL);
|
|
|
+ DataTypeApplyFromText(data_buf, data_type, p_data, format);
|
|
|
if (p_clamp_min || p_clamp_max)
|
|
|
{
|
|
|
if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0)
|
|
@@ -3391,12 +3420,12 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
|
|
char buf[64];
|
|
|
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
|
|
|
|
|
|
- bool value_changed = false;
|
|
|
- if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
|
|
|
- flags |= ImGuiInputTextFlags_CharsDecimal;
|
|
|
- flags |= ImGuiInputTextFlags_AutoSelectAll;
|
|
|
- flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
|
|
|
+ // Testing ActiveId as a minor optimization as filtering is not needed until active
|
|
|
+ if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
|
|
|
+ flags |= InputScalar_DefaultCharsFilter(data_type, format);
|
|
|
+ flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
|
|
|
|
|
|
+ bool value_changed = false;
|
|
|
if (p_step != NULL)
|
|
|
{
|
|
|
const float button_size = GetFrameHeight();
|
|
@@ -3823,7 +3852,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
|
|
|
if (c < 0x20)
|
|
|
{
|
|
|
bool pass = false;
|
|
|
- pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
|
|
|
+ pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
|
|
|
pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
|
|
|
if (!pass)
|
|
|
return false;
|
|
@@ -4209,16 +4238,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
if (state->SelectedAllMouseLock && !io.MouseDown[0])
|
|
|
state->SelectedAllMouseLock = false;
|
|
|
|
|
|
- // It is ill-defined whether the backend needs to send a \t character when pressing the TAB keys.
|
|
|
- // Win32 and GLFW naturally do it but not SDL.
|
|
|
+ // We except backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)
|
|
|
+ // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)
|
|
|
const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
|
|
|
if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly)
|
|
|
- if (!io.InputQueueCharacters.contains('\t'))
|
|
|
- {
|
|
|
- unsigned int c = '\t'; // Insert TAB
|
|
|
- if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
|
|
|
- state->OnKeyPressed((int)c);
|
|
|
- }
|
|
|
+ {
|
|
|
+ unsigned int c = '\t'; // Insert TAB
|
|
|
+ if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
|
|
|
+ state->OnKeyPressed((int)c);
|
|
|
+ }
|
|
|
|
|
|
// Process regular text input (before we check for Return because using some IME will effectively send a Return?)
|
|
|
// We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
|
|
@@ -4229,7 +4257,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
{
|
|
|
// Insert character if they pass filtering
|
|
|
unsigned int c = (unsigned int)io.InputQueueCharacters[n];
|
|
|
- if (c == '\t' && io.KeyShift)
|
|
|
+ if (c == '\t') // Skip Tab, see above.
|
|
|
continue;
|
|
|
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
|
|
|
state->OnKeyPressed((int)c);
|
|
@@ -4245,19 +4273,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
|
|
|
{
|
|
|
IM_ASSERT(state != NULL);
|
|
|
- IM_ASSERT(io.KeyMods == GetMergedKeyModFlags() && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); // We rarely do this check, but if anything let's do it here.
|
|
|
|
|
|
const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1);
|
|
|
state->Stb.row_count_per_page = row_count_per_page;
|
|
|
|
|
|
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
|
|
|
const bool is_osx = io.ConfigMacOSXBehaviors;
|
|
|
- const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiKeyModFlags_Super | ImGuiKeyModFlags_Shift));
|
|
|
+ const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiModFlags_Super | ImGuiModFlags_Shift));
|
|
|
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
|
|
|
const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
|
|
|
- const bool is_ctrl_key_only = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
|
|
|
- const bool is_shift_key_only = (io.KeyMods == ImGuiKeyModFlags_Shift);
|
|
|
- const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
|
|
|
+ const bool is_ctrl_key_only = (io.KeyMods == ImGuiModFlags_Ctrl);
|
|
|
+ const bool is_shift_key_only = (io.KeyMods == ImGuiModFlags_Shift);
|
|
|
+ const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiModFlags_Super) : (io.KeyMods == ImGuiModFlags_Ctrl);
|
|
|
|
|
|
const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
|
|
|
const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
|
|
@@ -5464,7 +5491,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
|
// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
|
|
|
// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
|
|
|
// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set.
|
|
|
-bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
|
|
|
+bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
if (window->SkipItems)
|
|
@@ -5472,11 +5499,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
|
|
|
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
const ImGuiID id = window->GetID(desc_id);
|
|
|
- float default_size = GetFrameHeight();
|
|
|
- if (size.x == 0.0f)
|
|
|
- size.x = default_size;
|
|
|
- if (size.y == 0.0f)
|
|
|
- size.y = default_size;
|
|
|
+ const float default_size = GetFrameHeight();
|
|
|
+ const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y);
|
|
|
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
|
|
|
ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
|
|
|
if (!ItemAdd(bb, id))
|
|
@@ -6949,7 +6973,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
|
|
{
|
|
|
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
|
|
|
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
|
|
|
- bool moving_toward_other_child_menu = false;
|
|
|
+ bool moving_toward_child_menu = false;
|
|
|
ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL;
|
|
|
if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar))
|
|
|
{
|
|
@@ -6960,18 +6984,22 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
|
|
ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
|
|
|
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack.
|
|
|
ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??)
|
|
|
- tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
|
|
|
+ tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle has maximum height to limit the slope and the bias toward large sub-menus
|
|
|
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f);
|
|
|
- moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
|
|
|
+ moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
|
|
|
//GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
|
|
|
}
|
|
|
- if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu)
|
|
|
+
|
|
|
+ // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)
|
|
|
+ // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.
|
|
|
+ // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)
|
|
|
+ if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu)
|
|
|
want_close = true;
|
|
|
|
|
|
// Open
|
|
|
if (!menu_is_open && pressed) // Click/activate to open
|
|
|
want_open = true;
|
|
|
- else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open
|
|
|
+ else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open
|
|
|
want_open = true;
|
|
|
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
|
|
|
{
|