ImGuiManager: Switch to dynamic fonts

This commit is contained in:
Stenzek 2025-04-12 19:51:28 +10:00
parent 5945153e67
commit 77457a3b1d
No known key found for this signature in database
26 changed files with 6504 additions and 3995 deletions

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,9 @@
#ifndef IMGUI_DISABLE
// Usage:
// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to enable support for imgui_freetype in imgui.
// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support
// for imgui_freetype in imgui. It is equivalent to selecting the default loader with:
// io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader()
// Optional support for OpenType SVG fonts:
// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927.
@ -14,44 +16,67 @@
// Forward declarations
struct ImFontAtlas;
struct ImFontBuilderIO;
struct ImFontLoader;
// Hinting greatly impacts visuals (and glyph sizes).
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
enum ImGuiFreeTypeBuilderFlags
// You can set those flags globally in ImFontAtlas::FontLoaderFlags
// You can set those flags on a per font basis in ImFontConfig::FontLoaderFlags
typedef unsigned int ImGuiFreeTypeLoaderFlags;
enum ImGuiFreeTypeLoaderFlags_
{
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 // Enable FreeType bitmap glyphs
ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeLoaderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9, // Enable FreeType bitmap glyphs
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiFreeTypeBuilderFlags_NoHinting = ImGuiFreeTypeLoaderFlags_NoHinting,
ImGuiFreeTypeBuilderFlags_NoAutoHint = ImGuiFreeTypeLoaderFlags_NoAutoHint,
ImGuiFreeTypeBuilderFlags_ForceAutoHint = ImGuiFreeTypeLoaderFlags_ForceAutoHint,
ImGuiFreeTypeBuilderFlags_LightHinting = ImGuiFreeTypeLoaderFlags_LightHinting,
ImGuiFreeTypeBuilderFlags_MonoHinting = ImGuiFreeTypeLoaderFlags_MonoHinting,
ImGuiFreeTypeBuilderFlags_Bold = ImGuiFreeTypeLoaderFlags_Bold,
ImGuiFreeTypeBuilderFlags_Oblique = ImGuiFreeTypeLoaderFlags_Oblique,
ImGuiFreeTypeBuilderFlags_Monochrome = ImGuiFreeTypeLoaderFlags_Monochrome,
ImGuiFreeTypeBuilderFlags_LoadColor = ImGuiFreeTypeLoaderFlags_LoadColor,
ImGuiFreeTypeBuilderFlags_Bitmap = ImGuiFreeTypeLoaderFlags_Bitmap,
#endif
};
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_;
#endif
namespace ImGuiFreeType
{
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
// If you need to dynamically select between multiple builders:
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
// - you can manually assign this builder with 'atlas->FontLoader = ImGuiFreeType::GetFontLoader()'
// - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data.
IMGUI_API const ImFontLoader* GetFontLoader();
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr);
// Obsolete names (will be removed soon)
// Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source)
IMGUI_API bool DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags);
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
//IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader()' if you need runtime selection.
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
#endif
}

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.9b
// dear imgui, v1.92.0 WIP
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@ -37,6 +37,7 @@ Index of this file:
// [SECTION] Tab bar, Tab item support
// [SECTION] Table support
// [SECTION] ImGui internal API
// [SECTION] ImFontLoader
// [SECTION] ImFontAtlas internal API
// [SECTION] Test Engine specific hooks (imgui_test_engine)
@ -132,7 +133,7 @@ Index of this file:
//-----------------------------------------------------------------------------
// Utilities
// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImPool<>, ImChunkStream<>)
// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImStableVector<>, ImPool<>, ImChunkStream<>)
struct ImBitVector; // Store 1-bit per value
struct ImRect; // An axis-aligned rectangle (2 points)
struct ImGuiTextIndex; // Maintain a line index for a text buffer.
@ -140,6 +141,9 @@ struct ImGuiTextIndex; // Maintain a line index for a text buffer.
// ImDrawList/ImFontAtlas
struct ImDrawDataBuilder; // Helper to build a ImDrawData instance
struct ImDrawListSharedData; // Data shared between all ImDrawList instances
struct ImFontAtlasBuilder; // Internal storage for incrementally packing and building a ImFontAtlas
struct ImFontAtlasPostProcessData; // Data available to potential texture post-processing functions
struct ImFontAtlasRectEntry; // Packed rectangle lookup entry
// ImGui
struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others)
@ -206,6 +210,10 @@ typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // F
typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest()
typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
// Table column indexing
typedef ImS16 ImGuiTableColumnIdx;
typedef ImU16 ImGuiTableDrawChannelIdx;
//-----------------------------------------------------------------------------
// [SECTION] Context pointer
// See implementation of this variable in imgui.cpp for comments and details.
@ -238,7 +246,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FONT(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FONT(...) do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context.
#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
// Static Asserts
@ -348,6 +356,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
// - Helper: ImBitArray
// - Helper: ImBitVector
// - Helper: ImSpan<>, ImSpanAllocator<>
// - Helper: ImStableVector<>
// - Helper: ImPool<>
// - Helper: ImChunkStream<>
// - Helper: ImGuiTextIndex
@ -379,6 +388,7 @@ IMGUI_API int ImStricmp(const char* str1, const char* str2);
IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); // Case insensitive compare to a certain count.
IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); // Copy to a certain count and always zero terminate (strncpy doesn't).
IMGUI_API char* ImStrdup(const char* str); // Duplicate a string.
IMGUI_API void* ImMemdup(const void* src, size_t size); // Duplicate a chunk of memory.
IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed.
IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); // Find first occurrence of 'c' in string range.
IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line
@ -492,6 +502,8 @@ static inline float ImTrunc(float f)
static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
static inline float ImTrunc64(float f) { return (float)(ImS64)(f); }
static inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); }
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
@ -524,6 +536,14 @@ struct ImVec1
constexpr ImVec1(float _x) : x(_x) { }
};
// Helper: ImVec2i (2D vector, integer)
struct ImVec2i
{
int x, y;
constexpr ImVec2i() : x(0), y(0) {}
constexpr ImVec2i(int _x, int _y) : x(_x), y(_y) {}
};
// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)
struct ImVec2ih
{
@ -675,6 +695,39 @@ struct ImSpanAllocator
inline void GetSpan(int n, ImSpan<T>* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }
};
// Helper: ImStableVector<>
// Allocating chunks of BLOCK_SIZE items. Objects pointers are never invalidated when growing, only by clear().
// Important: does not destruct anything!
// Implemented only the minimum set of functions we need for it.
template<typename T, int BLOCK_SIZE>
struct ImStableVector
{
int Size = 0;
int Capacity = 0;
ImVector<T*> Blocks;
// Functions
inline ~ImStableVector() { for (T* block : Blocks) IM_FREE(block); }
inline void clear() { Size = Capacity = 0; Blocks.clear_delete(); }
inline void resize(int new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; }
inline void reserve(int new_cap)
{
new_cap = IM_MEMALIGN(new_cap, BLOCK_SIZE);
int old_count = Capacity / BLOCK_SIZE;
int new_count = new_cap / BLOCK_SIZE;
if (new_count <= old_count)
return;
Blocks.resize(new_count);
for (int n = old_count; n < new_count; n++)
Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCK_SIZE);
Capacity = new_cap;
}
inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCK_SIZE); void* ptr = &Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; }
};
// Helper: ImPool<>
// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
@ -783,17 +836,20 @@ IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStorag
// You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
struct IMGUI_API ImDrawListSharedData
{
ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel)
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas (== FontAtlas->TexUvLines)
ImFont* Font; // Current/default font (optional, for simplified AddText overload)
float FontSize; // Current/default font size (optional, for simplified AddText overload)
float FontScale; // Current/default font scale (== FontSize / Font->FontSize)
float FontWeight; // Current/default font weight
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo()
float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
float InitialFringeScale; // Initial scale to apply to AA fringe
ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
ImVector<ImVec2> TempBuffer; // Temporary write buffer
ImVector<ImDrawList*> DrawLists; // All draw lists associated to this ImDrawListSharedData
ImGuiContext* Context; // [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case.
// Lookup tables
ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
@ -801,6 +857,7 @@ struct IMGUI_API ImDrawListSharedData
ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
ImDrawListSharedData();
~ImDrawListSharedData();
void SetCircleTessellationMaxError(float max_error);
};
@ -812,6 +869,14 @@ struct ImDrawDataBuilder
ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); }
};
struct ImFontStackData
{
ImFont* Font;
float FontSizeBeforeScaling; // ~~ style.FontSizeBase
float FontSizeAfterScaling; // ~~ g.FontSize
float FontWeight;
};
//-----------------------------------------------------------------------------
// [SECTION] Style support
//-----------------------------------------------------------------------------
@ -994,9 +1059,11 @@ enum ImGuiSelectableFlagsPrivate_
// Extend ImGuiTreeNodeFlags_
enum ImGuiTreeNodeFlagsPrivate_
{
ImGuiTreeNodeFlags_NoNavFocus = 1 << 27,// Don't claim nav focus when interacting with this item (#8551)
ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()
ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517)
ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,
ImGuiTreeNodeFlags_DrawLinesMask_ = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes,
};
enum ImGuiSeparatorFlags_
@ -1283,15 +1350,18 @@ struct ImGuiLastItemData
};
// Store data emitted by TreeNode() for usage by TreePop()
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere: store the minimum amount of data
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsToParent: store the minimum amount of data
// which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult().
// Only stored when the node is a potential candidate for landing on a Left arrow jump.
struct ImGuiTreeNodeStackData
{
ImGuiID ID;
ImGuiTreeNodeFlags TreeFlags;
ImGuiItemFlags ItemFlags; // Used for nav landing
ImRect NavRect; // Used for nav landing
ImGuiItemFlags ItemFlags; // Used for nav landing
ImRect NavRect; // Used for nav landing
float DrawLinesX1;
float DrawLinesToNodesY2;
ImGuiTableColumnIdx DrawLinesTableColumn;
};
// sizeof() = 20
@ -2007,10 +2077,12 @@ struct ImGuiMetricsConfig
bool ShowDrawCmdMesh = true;
bool ShowDrawCmdBoundingBoxes = true;
bool ShowTextEncodingViewer = false;
bool ShowTextureUsedRect = false;
int ShowWindowsRectsType = -1;
int ShowTablesRectsType = -1;
int HighlightMonitorIdx = -1;
ImGuiID HighlightViewportID = 0;
bool ShowFontPreview = true;
};
struct ImGuiStackLevelInfo
@ -2063,15 +2135,18 @@ struct ImGuiContextHook
struct ImGuiContext
{
bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO;
ImGuiPlatformIO PlatformIO;
ImGuiStyle Style;
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height.
float FontScale; // == FontSize / Font->FontSize
float CurrentDpiScale; // Current window/viewport DpiScale
ImVector<ImFontAtlas*> FontAtlases; // List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas)
ImFont* Font; // Currently bound font. (== FontStack.back().Font)
ImFontBaked* FontBaked; // Currently bound font at currently bound size. (== Font->GetFontBaked(FontSize))
float FontSize; // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function).
float FontSizeBase; // Font size before scaling == style.FontSizeBase == value passed to PushFont() / PushFontSize() when specified.
float FontBakedScale; // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f.
float FontRasterizerDensity; // Current font density. Used by all calls to GetFontBaked().
float FontWeight;
float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale
ImDrawListSharedData DrawListSharedData;
double Time;
int FrameCount;
@ -2172,7 +2247,7 @@ struct ImGuiContext
ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line)
ImVector<ImGuiColorMod> ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
ImVector<ImGuiStyleMod> StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
ImVector<ImFontStackData> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
ImVector<ImGuiFocusScopeData> FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
ImVector<ImGuiItemFlags> ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
ImVector<ImGuiGroupData> GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
@ -2240,6 +2315,7 @@ struct ImGuiContext
bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
bool ConfigNavWindowingWithGamepad; // = true. Enable CTRL+TAB by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer.
ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)
ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
@ -2247,6 +2323,7 @@ struct ImGuiContext
ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents
float NavWindowingTimer;
float NavWindowingHighlightAlpha;
ImGuiInputSource NavWindowingInputSource;
bool NavWindowingToggleLayer;
ImGuiKey NavWindowingToggleKey;
ImVec2 NavWindowingAccumDeltaPos;
@ -2317,7 +2394,8 @@ struct ImGuiContext
// Widget state
ImGuiInputTextState InputTextState;
ImGuiInputTextDeactivatedState InputTextDeactivatedState;
ImFont InputTextPasswordFont;
ImFontBaked InputTextPasswordFontBackupBaked;
ImFontFlags InputTextPasswordFontBackupFlags;
ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types
int BeginMenuDepth;
@ -2350,9 +2428,13 @@ struct ImGuiContext
ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest()
// Platform support
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame. Will be applied at end of the frame. For some backends, this is required to have WantVisible=true in order to receive text message.
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler.
// Extensions
// FIXME: We could provide an API to register one slot in an array held in ImGuiContext?
ImVector<ImTextureData*> UserTextures; // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[].
// Settings
bool SettingsLoaded;
float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero
@ -2418,7 +2500,7 @@ struct ImGuiContext
float FramerateSecPerFrameAccum;
int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.
int WantCaptureKeyboardNextFrame; // "
int WantTextInputNextFrame;
int WantTextInputNextFrame; // Copied in EndFrame() from g.PlatformImeData.WanttextInput. Needs to be set for some backends (SDL3) to emit character inputs.
ImVector<char> TempBuffer; // Temporary text buffer
char TempKeychordName[64];
@ -2464,7 +2546,8 @@ struct IMGUI_API ImGuiWindowTempData
ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement
int TreeDepth; // Current tree depth.
ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
ImU32 TreeRecordsClippedNodesY2Mask; // Store whether we should keep recording Y2. Cleared when passing clip max. Equivalent TreeHasStackDataDepthMask value should always be set.
ImVector<ImGuiWindow*> ChildWindows;
ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state)
ImGuiOldColumns* CurrentColumns; // Current columns set
@ -2609,9 +2692,11 @@ public:
// We don't use g.FontSize because the window may be != g.CurrentWindow.
ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontBaseSize * FontWindowScale * FontWindowScaleParents; }
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); }
ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); }
// [Obsolete] ImGuiWindow::CalcFontSize() was removed in 1.92.x because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window.
//float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontWindowScaleParents;
};
//-----------------------------------------------------------------------------
@ -2701,11 +2786,7 @@ struct IMGUI_API ImGuiTabBar
//-----------------------------------------------------------------------------
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
#define IMGUI_TABLE_MAX_COLUMNS 512 // May be further lifted
// Our current column maximum is 64 but we may raise that in the future.
typedef ImS16 ImGuiTableColumnIdx;
typedef ImU16 ImGuiTableDrawChannelIdx;
#define IMGUI_TABLE_MAX_COLUMNS 512 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
// [Internal] sizeof() ~ 112
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
@ -3038,9 +3119,18 @@ namespace ImGui
IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
// Fonts, drawing
IMGUI_API void SetCurrentFont(ImFont* font);
inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture
IMGUI_API void UnregisterUserTexture(ImTextureData* tex);
IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling, float font_weight);
IMGUI_API void UpdateCurrentFontSize(float restore_font_size_after_scaling);
IMGUI_API void SetFontRasterizerDensity(float rasterizer_density);
inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }
inline float GetRoundedFontSize(float size) { return IM_ROUND(size); }
IMGUI_API ImFont* GetDefaultFont();
IMGUI_API void PushPasswordFont();
IMGUI_API void PopPasswordFont();
inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
@ -3052,7 +3142,7 @@ namespace ImGui
// NewFrame
IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs);
IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
IMGUI_API void UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos);
IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window);
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
IMGUI_API void UpdateMouseMovingWindowNewFrame();
@ -3178,7 +3268,7 @@ namespace ImGui
IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data);
IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
@ -3341,6 +3431,8 @@ namespace ImGui
IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel();
IMGUI_API void TablePushColumnChannel(int column_n);
IMGUI_API void TablePopColumnChannel();
IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);
// Tables: Internals
@ -3417,7 +3509,7 @@ namespace ImGui
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
@ -3436,11 +3528,15 @@ namespace ImGui
IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
// Widgets
// Widgets: Text
IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
IMGUI_API void TextAligned(float align_x, float size_x, const char* fmt, ...); // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
IMGUI_API void TextAlignedV(float align_x, float size_x, const char* fmt, va_list args);
// Widgets
IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);
IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);
@ -3464,6 +3560,8 @@ namespace ImGui
// Widgets: Tree Nodes
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
IMGUI_API void TreeNodeDrawLineToChildNode(const ImVec2& target_pos);
IMGUI_API void TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);
IMGUI_API void TreePushOverrideID(ImGuiID id);
IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id);
IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open);
@ -3544,7 +3642,9 @@ namespace ImGui
IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);
IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
IMGUI_API void DebugNodeFont(ImFont* font);
IMGUI_API void DebugNodeFontGlyphesForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask);
IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);
IMGUI_API void DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect = NULL); // ID used to facilitate persisting the "current" texture.
IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label);
IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
IMGUI_API void DebugNodeTable(ImGuiTable* table);
@ -3577,31 +3677,188 @@ namespace ImGui
} // namespace ImGui
//-----------------------------------------------------------------------------
// [SECTION] ImFontLoader
//-----------------------------------------------------------------------------
// Hooks and storage for a given font backend.
// This structure is likely to evolve as we add support for incremental atlas updates.
// Conceptually this could be public, but API is still going to be evolve.
struct ImFontLoader
{
const char* Name;
bool (*LoaderInit)(ImFontAtlas* atlas);
void (*LoaderShutdown)(ImFontAtlas* atlas);
bool (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src);
void (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src);
bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);
bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph);
// Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.
// FIXME: At this point the two other types of buffers may be managed by core to be consistent?
size_t FontBakedSrcLoaderDataSize;
ImFontLoader() { memset(this, 0, sizeof(*this)); }
};
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype();
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are not actually compatible but we provide this as a compile-time error report helper.
#endif
//-----------------------------------------------------------------------------
// [SECTION] ImFontAtlas internal API
//-----------------------------------------------------------------------------
// This structure is likely to evolve as we add support for incremental atlas updates.
// Conceptually this could be in ImGuiPlatformIO, but we are far from ready to make this public.
struct ImFontBuilderIO
// Helpers: ImTextureRef ==/!= operators provided as convenience
// (note that _TexID and _TexData are never set simultaneously)
static inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; }
static inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; }
// Refer to ImFontAtlasPackGetRect() to better understand how this works.
#define ImFontAtlasRectId_IndexMask_ (0x000FFFFF) // 20-bits: index to access builder->RectsIndex[].
#define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.
#define ImFontAtlasRectId_GenerationShift_ (20)
inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return id & ImFontAtlasRectId_IndexMask_; }
inline int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx < ImFontAtlasRectId_IndexMask_ && gen_idx < (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)
// User are returned ImFontAtlasRectId values which are meant to be persistent.
// We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId.
// RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code.
// Having this also makes it easier to e.g. sort rectangles during repack.
struct ImFontAtlasRectEntry
{
bool (*FontBuilder_Build)(ImFontAtlas* atlas);
int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list.
int Generation : 10; // Increased each time the entry is reused for a new rectangle.
unsigned int IsUsed : 1;
};
// Helper for font builder
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype();
// Data available to potential texture post-processing functions
struct ImFontAtlasPostProcessData
{
ImFontAtlas* FontAtlas;
ImFont* Font;
ImFontConfig* FontSrc;
ImFontBaked* FontBaked;
ImFontGlyph* Glyph;
// Pixel data
unsigned char* Pixels;
ImTextureFormat Format;
int Pitch;
int Width;
int Height;
};
// We avoid dragging imstb_rectpack.h into public header (partly because binding generators are having issues with it)
#ifdef IMGUI_STB_NAMESPACE
namespace IMGUI_STB_NAMESPACE { struct stbrp_node; }
typedef IMGUI_STB_NAMESPACE::stbrp_node stbrp_node_im;
#else
struct stbrp_node;
typedef stbrp_node stbrp_node_im;
#endif
struct stbrp_context_opaque { char data[80]; };
// Internal storage for incrementally packing and building a ImFontAtlas
struct ImFontAtlasBuilder
{
stbrp_context_opaque PackContext; // Actually 'stbrp_context' but we don't want to define this in the header file.
ImVector<stbrp_node_im> PackNodes;
ImVector<ImTextureRect> Rects;
ImVector<ImFontAtlasRectEntry> RectsIndex; // ImFontAtlasRectId -> index into Rects[]
ImVector<unsigned char> TempBuffer; // Misc scratch buffer
int RectsIndexFreeListStart;// First unused entry
int RectsPackedCount; // Number of packed rectangles.
int RectsPackedSurface; // Number of packed pixels. Used when compacting to heuristically find the ideal texture size.
int RectsDiscardedCount;
int RectsDiscardedSurface;
int FrameCount; // Current frame count
ImVec2i MaxRectSize; // Largest rectangle to pack (de-facto used as a "minimum texture size")
ImVec2i MaxRectBounds; // Bottom-right most used pixels
bool LockDisableResize; // Disable resizing texture
bool PreloadedAllGlyphsRanges; // Set when missing ImGuiBackendFlags_RendererHasTextures features forces atlas to preload everything.
// Cache of all ImFontBaked
ImStableVector<ImFontBaked,32> BakedPool;
ImGuiStorage BakedMap; // BakedId --> ImFontBaked*
int BakedDiscardedCount;
// Custom rectangle identifiers
ImFontAtlasRectId PackIdMouseCursors; // White pixel + mouse cursors. Also happen to be fallback in case of packing failure.
ImFontAtlasRectId PackIdLinesTexData;
ImFontAtlasBuilder() { memset(this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; }
};
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildDestroy(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildMain(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader);
IMGUI_API void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char);
IMGUI_API void ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects
IMGUI_API ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h);
IMGUI_API void ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h);
IMGUI_API void ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_w = -1, int old_h = -1);
IMGUI_API void ImFontAtlasTextureCompact(ImFontAtlas* atlas);
IMGUI_API ImVec2i ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
IMGUI_API void ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy
IMGUI_API void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v);
IMGUI_API void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames);
IMGUI_API bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src);
IMGUI_API void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
IMGUI_API void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src);
IMGUI_API bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed
IMGUI_API void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font);
IMGUI_API void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames);
IMGUI_API ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float baked_weight, float rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_weight, float font_rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_weight, float font_rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_weight, float font_rasterizer_density, ImGuiID baked_id);
IMGUI_API void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked);
IMGUI_API ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);
IMGUI_API void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);
IMGUI_API void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch);
IMGUI_API void ImFontAtlasPackInit(ImFontAtlas* atlas);
IMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry = NULL);
IMGUI_API ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures);
IMGUI_API void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
IMGUI_API void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
IMGUI_API void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex);
IMGUI_API void ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data);
IMGUI_API void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor);
IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col);
IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);
IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);
IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status);
IMGUI_API const char* ImTextureDataGetFormatName(ImTextureFormat format);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
IMGUI_API void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas);
#endif
IMGUI_API void ImFontAtlasUpdateSourcesPointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);
IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value);
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
IMGUI_API void ImFontAtlasBuildGetOversampleFactors(const ImFontConfig* src, int* out_oversample_h, int* out_oversample_v);
IMGUI_API bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]);

View File

@ -141,6 +141,7 @@
// with previous char)
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
// (return type is int, -1 means not valid to insert)
// (not supported if you want to use UTF-8, see below)
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
// as manually wordwrapping for end-of-line positioning
@ -178,6 +179,13 @@
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
//
// To support UTF-8:
//
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
// Instead, call stb_textedit_text() directly for text contents.
//
// Keyboard input must be encoded as a single integer value; e.g. a character code
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
@ -250,8 +258,10 @@
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: [DEAR IMGUI] added 2024-09
// call this to text inputs sent to the textfield.
// text: (added 2025)
// call this to directly send text input the textfield, which is required
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// cannot infer text length.
//
//
// When rendering, you can read the cursor position and selection state from
@ -400,6 +410,16 @@ typedef struct
#define IMSTB_TEXTEDIT_memmove memmove
#endif
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
#endif
/////////////////////////////////////////////////////////////////////////////
//
@ -648,17 +668,6 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
}
}
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
#endif
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
@ -668,9 +677,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
--c;
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
while (c >= 0 && !is_word_boundary(str, c))
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
if( c < 0 )
c = 0;
@ -684,9 +693,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
while( c < len && !is_word_boundary( str, c ) )
++c;
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
if( c > len )
c = len;
@ -739,6 +748,7 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
#define STB_TEXTEDIT_KEYTYPE int
#endif
// API key: process text input
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
@ -753,8 +763,7 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
else {
} else {
stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
stb_text_makeundo_insert(state, state->cursor, text_len);
@ -771,6 +780,7 @@ retry:
switch (key) {
default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
// This is not suitable for UTF-8 support.
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
@ -918,8 +928,9 @@ retry:
state->cursor = start;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@ -927,7 +938,8 @@ retry:
x += dx;
if (x > goal_x)
break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@ -980,8 +992,9 @@ retry:
state->cursor = find.prev_first;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@ -989,7 +1002,8 @@ retry:
x += dx;
if (x > goal_x)
break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@ -1002,8 +1016,13 @@ retry:
// go to previous line
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
--prev_scan;
while (prev_scan > 0)
{
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
prev_scan = prev;
}
find.first_char = find.prev_first;
find.prev_first = prev_scan;
}
@ -1082,7 +1101,7 @@ retry:
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
@ -1094,9 +1113,9 @@ retry:
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = n;
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
}
@ -1110,7 +1129,7 @@ retry:
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@ -1125,7 +1144,7 @@ retry:
if (state->single_line)
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.9b
// dear imgui, v1.92.0 WIP
// (demo code)
// Help:
@ -82,6 +82,7 @@ Index of this file:
// [SECTION] DemoWindowWidgetsDisableBlocks()
// [SECTION] DemoWindowWidgetsDragAndDrop()
// [SECTION] DemoWindowWidgetsDragsAndSliders()
// [SECTION] DemoWindowWidgetsFonts()
// [SECTION] DemoWindowWidgetsImages()
// [SECTION] DemoWindowWidgetsListBoxes()
// [SECTION] DemoWindowWidgetsMultiComponents()
@ -409,9 +410,19 @@ void ImGui::ShowDemoWindow(bool* p_open)
return;
}
// Most "big" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details.
ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets.
//ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align)
// Most framed widgets share a common width settings. Remaining width is used for the label.
// The width of the frame may be changed with PushItemWidth() or SetNextItemWidth().
// - Positive value for absolute size, negative value for right-alignment.
// - The default value is about GetWindowWidth() * 0.65f.
// - See 'Demo->Layout->Widgets Width' for details.
// Here we change the frame width based on how much width we want to give to the label.
const float label_width_base = ImGui::GetFontSize() * 12; // Some amount of width for label, based on font size.
const float label_width_max = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets.
const float label_width = IM_MIN(label_width_base, label_width_max);
ImGui::PushItemWidth(-label_width); // Right-align: framed items will leave 'label_width' available for the label.
//ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for framed widgets, leaving 60% width for labels.
//ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for labels, leaving 60% width for framed widgets.
//ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Use XXX width for labels, leaving the rest for framed widgets.
// Menu Bar
DemoWindowMenuBar(&demo_data);
@ -565,14 +576,15 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);
ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);
ImGui::CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);
ImGui::EndDisabled();
ImGui::TreePop();
ImGui::Spacing();
}
IMGUI_DEMO_MARKER("Configuration/Style");
if (ImGui::TreeNode("Style"))
IMGUI_DEMO_MARKER("Configuration/Style, Fonts");
if (ImGui::TreeNode("Style, Fonts"))
{
ImGui::Checkbox("Style Editor", &demo_data.ShowStyleEditor);
ImGui::SameLine();
@ -1719,6 +1731,25 @@ static void DemoWindowWidgetsDragsAndSliders()
}
}
//-----------------------------------------------------------------------------
// [SECTION] DemoWindowWidgetsFonts()
//-----------------------------------------------------------------------------
// Forward declare ShowFontAtlas() which isn't worth putting in public API yet
namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }
static void DemoWindowWidgetsFonts()
{
IMGUI_DEMO_MARKER("Widgets/Fonts");
if (ImGui::TreeNode("Fonts"))
{
ImFontAtlas* atlas = ImGui::GetIO().Fonts;
ImGui::ShowFontAtlas(atlas);
// FIXME-NEWATLAS: Provide a demo to add/create a procedural font?
ImGui::TreePop();
}
}
//-----------------------------------------------------------------------------
// [SECTION] DemoWindowWidgetsImages()
//-----------------------------------------------------------------------------
@ -1735,13 +1766,12 @@ static void DemoWindowWidgetsImages()
"Hover the texture for a zoomed view!");
// Below we are displaying the font texture because it is the only texture we have access to inside the demo!
// Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that
// will be passed to the rendering backend via the ImDrawCmd structure.
// Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers.
// If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
// of their respective source file to specify what they expect to be stored in ImTextureID, for example:
// - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer
// of their respective source file to specify what they are using as texture identifier, for example:
// - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer.
// - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
// More:
// So with the DirectX11 backend, you call ImGui::Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID.
// - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
// to ImGui::Image(), and gather width/height through your own functions, etc.
// - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
@ -1749,14 +1779,19 @@ static void DemoWindowWidgetsImages()
// - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
// - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
// - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
ImTextureID my_tex_id = io.Fonts->TexID;
float my_tex_w = (float)io.Fonts->TexWidth;
float my_tex_h = (float)io.Fonts->TexHeight;
// Grab the current texture identifier used by the font atlas.
ImTextureRef my_tex_id = io.Fonts->TexRef;
// Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.
float my_tex_w = (float)io.Fonts->TexData->Width;
float my_tex_h = (float)io.Fonts->TexData->Height;
{
ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
ImVec2 pos = ImGui::GetCursorScreenPos();
ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
if (ImGui::BeginItemTooltip())
@ -2388,7 +2423,7 @@ static const char* ExampleNames[] =
struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage
{
// Find which item should be Focused after deletion.
// Call _before_ item submission. Retunr an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
// Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
// The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
// - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
// - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.
@ -2491,7 +2526,7 @@ struct ExampleDualListBox
{
const int* a = (const int*)lhs;
const int* b = (const int*)rhs;
return (*a - *b) > 0 ? +1 : -1;
return (*a - *b);
}
void SortItems(int n)
{
@ -2499,7 +2534,7 @@ struct ExampleDualListBox
}
void Show()
{
//ImGui::Checkbox("Sorted", &OptKeepSorted);
//if (ImGui::Checkbox("Sorted", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); }
if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None))
{
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side
@ -2971,7 +3006,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection)
{
ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Enable pressing left to jump to parent
tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Enable pressing left to jump to parent
if (node->Childs.Size == 0)
tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf;
if (selection->Contains((ImGuiID)node->UID))
@ -3659,13 +3694,13 @@ static void DemoWindowWidgetsTextInput()
}
};
static char buf1[32] = ""; ImGui::InputText("default", buf1, 32);
static char buf2[32] = ""; ImGui::InputText("decimal", buf2, 32, ImGuiInputTextFlags_CharsDecimal);
static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, 32, ImGuiInputTextFlags_CharsUppercase);
static char buf5[32] = ""; ImGui::InputText("no blank", buf5, 32, ImGuiInputTextFlags_CharsNoBlank);
static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.
static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.
static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_ARRAYSIZE(buf1));
static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CharsDecimal);
static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_ARRAYSIZE(buf4), ImGuiInputTextFlags_CharsUppercase);
static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_ARRAYSIZE(buf5), ImGuiInputTextFlags_CharsNoBlank);
static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_ARRAYSIZE(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.
static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_ARRAYSIZE(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.
ImGui::TreePop();
}
@ -3721,20 +3756,20 @@ static void DemoWindowWidgetsTextInput()
}
};
static char buf1[64];
ImGui::InputText("Completion", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);
ImGui::InputText("Completion", buf1, IM_ARRAYSIZE(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);
ImGui::SameLine(); HelpMarker(
"Here we append \"..\" each time Tab is pressed. "
"See 'Examples>Console' for a more meaningful demonstration of using this callback.");
static char buf2[64];
ImGui::InputText("History", buf2, 64, ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);
ImGui::InputText("History", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);
ImGui::SameLine(); HelpMarker(
"Here we replace and select text each time Up/Down are pressed. "
"See 'Examples>Console' for a more meaningful demonstration of using this callback.");
static char buf3[64];
static int edit_count = 0;
ImGui::InputText("Edit", buf3, 64, ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);
ImGui::InputText("Edit", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);
ImGui::SameLine(); HelpMarker(
"Here we toggle the casing of the first character on every edit + count edits.");
ImGui::SameLine(); ImGui::Text("(%d)", edit_count);
@ -3920,6 +3955,7 @@ static void DemoWindowWidgetsTreeNodes()
IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
if (ImGui::TreeNode("Tree Nodes"))
{
// See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees");
if (ImGui::TreeNode("Basic trees"))
{
@ -3946,6 +3982,35 @@ static void DemoWindowWidgetsTreeNodes()
ImGui::TreePop();
}
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines");
if (ImGui::TreeNode("Hierarchy lines"))
{
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
if (ImGui::TreeNodeEx("Parent", base_flags))
{
if (ImGui::TreeNodeEx("Child 1", base_flags))
{
ImGui::Button("Button for Child 1");
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Child 2", base_flags))
{
ImGui::Button("Button for Child 2");
ImGui::TreePop();
}
ImGui::Text("Remaining contents");
ImGui::Text("Remaining contents");
ImGui::TreePop();
}
ImGui::TreePop();
}
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
if (ImGui::TreeNode("Advanced, with Selectable nodes"))
{
@ -3963,7 +4028,13 @@ static void DemoWindowWidgetsTreeNodes()
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only.");
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)");
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsBackHere", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsBackHere);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent);
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop);
ImGui::Text("Hello!");
@ -4146,6 +4217,7 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)
DemoWindowWidgetsDragAndDrop();
DemoWindowWidgetsDragsAndSliders();
DemoWindowWidgetsFonts();
DemoWindowWidgetsImages();
DemoWindowWidgetsListBoxes();
DemoWindowWidgetsMultiComponents();
@ -4370,6 +4442,17 @@ static void DemoWindowLayout()
}
ImGui::PopItemWidth();
ImGui::Text("SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))");
ImGui::PushItemWidth(-IM_MIN(ImGui::GetFontSize() * 12, ImGui::GetContentRegionAvail().x * 0.40f));
ImGui::DragFloat("float##4a", &f);
if (show_indented_items)
{
ImGui::Indent();
ImGui::DragFloat("float (indented)##4b", &f);
ImGui::Unindent();
}
ImGui::PopItemWidth();
// Demonstrate using PushItemWidth to surround three items.
// Calling SetNextItemWidth() before each of them would have the same effect.
ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)");
@ -4607,10 +4690,11 @@ static void DemoWindowLayout()
ImGui::SmallButton("SmallButton()");
// Tree
// (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline)
const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
ImGui::Button("Button##1");
ImGui::SameLine(0.0f, spacing);
if (ImGui::TreeNode("Node##1"))
if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone))
{
// Placeholder tree data
for (int i = 0; i < 6; i++)
@ -4992,7 +5076,7 @@ static void DemoWindowLayout()
case 2:
ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), ImGui::GetFontWeight(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
break;
}
}
@ -5399,7 +5483,7 @@ struct MyItem
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
}
// qsort() is instable so always return a way to differenciate items.
// qsort() is instable so always return a way to differentiate items.
// Your own compare function may want to avoid fallback on implicit sort specs.
// e.g. a Name compare if it wasn't already part of the sort specs.
return (a->ID - b->ID);
@ -6592,7 +6676,7 @@ static void DemoWindowTables()
{
static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns;
static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull;
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanLabelWidth);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanAllColumns);
@ -7762,7 +7846,7 @@ static void DemoWindowInputs()
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
// 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active)
// (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h)
// (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
//char str[16] = "Press CTRL+A";
//ImGui::Spacing();
//ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
@ -7789,7 +7873,7 @@ static void DemoWindowInputs()
{
ImGui::Text("(in PopupF)");
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
// (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h)
// (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
//ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
//ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "...");
ImGui::EndPopup();
@ -7963,7 +8047,7 @@ void ImGui::ShowAboutWindow(bool* p_open)
if (copy_to_clipboard)
{
ImGui::LogToClipboard();
ImGui::LogText("```\n"); // Back quotes will make text appears without formatting when pasting on GitHub
ImGui::LogText("```cpp\n"); // Back quotes will make text appears without formatting when pasting on GitHub
}
ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
@ -8059,8 +8143,10 @@ void ImGui::ShowAboutWindow(bool* p_open)
if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors");
if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos");
if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset");
if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) ImGui::Text(" RendererHasTextures");
ImGui::Separator();
ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight);
ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height);
ImGui::Text("io.Fonts->FontLoaderName: \"%s\"", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL");
ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
ImGui::Separator();
@ -8085,41 +8171,10 @@ void ImGui::ShowAboutWindow(bool* p_open)
//-----------------------------------------------------------------------------
// [SECTION] Style Editor / ShowStyleEditor()
//-----------------------------------------------------------------------------
// - ShowFontSelector()
// - ShowStyleSelector()
// - ShowStyleEditor()
//-----------------------------------------------------------------------------
// Forward declare ShowFontAtlas() which isn't worth putting in public API yet
namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }
// Demo helper function to select among loaded fonts.
// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.
void ImGui::ShowFontSelector(const char* label)
{
ImGuiIO& io = ImGui::GetIO();
ImFont* font_current = ImGui::GetFont();
if (ImGui::BeginCombo(label, font_current->GetDebugName()))
{
for (ImFont* font : io.Fonts->Fonts)
{
ImGui::PushID((void*)font);
if (ImGui::Selectable(font->GetDebugName(), font == font_current))
io.FontDefault = font;
if (font == font_current)
ImGui::SetItemDefaultFocus();
ImGui::PopID();
}
ImGui::EndCombo();
}
ImGui::SameLine();
HelpMarker(
"- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n"
"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
"- Read FAQ and docs/FONTS.md for more details.\n"
"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
}
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
// Here we use the simplified Combo() api that packs items into a single literal string.
// Useful for quick combo boxes where the choices are known locally.
@ -8139,12 +8194,21 @@ bool ImGui::ShowStyleSelector(const char* label)
return false;
}
static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
{
if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return "DrawLinesNone";
if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return "DrawLinesFull";
if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return "DrawLinesToNodes";
return "";
}
// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
void ImGui::ShowStyleEditor(ImGuiStyle* ref)
{
IMGUI_DEMO_MARKER("Tools/Style Editor");
// You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
// (without a reference style pointer, we will use one compared locally as a reference)
ImGuiStyle& style = ImGui::GetStyle();
ImGuiStyle& style = GetStyle();
static ImGuiStyle ref_saved_style;
// Default to using internal storage as reference
@ -8155,194 +8219,224 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
if (ref == NULL)
ref = &ref_saved_style;
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
PushItemWidth(GetWindowWidth() * 0.50f);
if (ImGui::ShowStyleSelector("Colors##Selector"))
ref_saved_style = style;
ImGui::ShowFontSelector("Fonts##Selector");
{
// General
SeparatorText("General");
if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
// Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
{ bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine();
{ bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine();
{ bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
if (ShowStyleSelector("Colors##Selector"))
ref_saved_style = style;
ShowFontSelector("Fonts##Selector");
if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f"))
style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize());
DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);
//BeginDisabled(GetIO().ConfigDpiScaleFonts);
DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 5.0f);
//SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
//EndDisabled();
// Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
if (SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
{ bool border = (style.WindowBorderSize > 0.0f); if (Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
SameLine();
{ bool border = (style.FrameBorderSize > 0.0f); if (Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
SameLine();
{ bool border = (style.PopupBorderSize > 0.0f); if (Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
}
// Save/Revert button
if (ImGui::Button("Save Ref"))
if (Button("Save Ref"))
*ref = ref_saved_style = style;
ImGui::SameLine();
if (ImGui::Button("Revert Ref"))
SameLine();
if (Button("Revert Ref"))
style = *ref;
ImGui::SameLine();
SameLine();
HelpMarker(
"Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
"Use \"Export\" below to save them somewhere.");
ImGui::Separator();
if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None))
SeparatorText("Details");
if (BeginTabBar("##tabs", ImGuiTabBarFlags_None))
{
if (ImGui::BeginTabItem("Sizes"))
if (BeginTabItem("Sizes"))
{
ImGui::SeparatorText("Main");
ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
SeparatorText("Main");
SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
ImGui::SeparatorText("Borders");
ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
SeparatorText("Borders");
SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SeparatorText("Rounding");
ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
SeparatorText("Rounding");
SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
ImGui::SeparatorText("Tabs");
ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f");
ImGui::SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f");
ImGui::SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
ImGui::DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
ImGui::DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
SeparatorText("Tabs");
SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f");
SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f");
SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
ImGui::SeparatorText("Tables");
ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f);
ImGui::SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
SeparatorText("Tables");
SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f);
SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
ImGui::SeparatorText("Windows");
ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
ImGui::SliderFloat("WindowBorderHoverPadding", &style.WindowBorderHoverPadding, 1.0f, 20.0f, "%.0f");
SeparatorText("Trees");
bool combo_open = BeginCombo("TreeLinesFlags", GetTreeLinesFlagsName(style.TreeLinesFlags));
SameLine();
HelpMarker("[Experimental] Tree lines may not work in all situations (e.g. using a clipper) and may incurs slight traversal overhead.\n\nImGuiTreeNodeFlags_DrawLinesFull is faster than ImGuiTreeNodeFlags_DrawLinesToNode.");
if (combo_open)
{
const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes };
for (ImGuiTreeNodeFlags option : options)
if (Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option))
style.TreeLinesFlags = option;
EndCombo();
}
SliderFloat("TreeLinesSize", &style.TreeLinesSize, 0.0f, 2.0f, "%.0f");
SliderFloat("TreeLinesRounding", &style.TreeLinesRounding, 0.0f, 12.0f, "%.0f");
SeparatorText("Windows");
SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
SliderFloat("WindowBorderHoverPadding", &style.WindowBorderHoverPadding, 1.0f, 20.0f, "%.0f");
int window_menu_button_position = style.WindowMenuButtonPosition + 1;
if (ImGui::Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0"))
if (Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0"))
style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);
ImGui::SeparatorText("Widgets");
ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.");
ImGui::SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
ImGui::SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f");
ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f");
ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, 1.0f, "%.0f");
SeparatorText("Widgets");
Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.");
SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f");
SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f");
SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, 1.0f, "%.0f");
ImGui::SeparatorText("Tooltips");
SeparatorText("Tooltips");
for (int n = 0; n < 2; n++)
if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav"))
if (TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav"))
{
ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone);
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort);
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal);
ImGui::CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary);
ImGui::CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay);
ImGui::TreePop();
CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone);
CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort);
CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal);
CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary);
CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay);
TreePop();
}
ImGui::SeparatorText("Misc");
ImGui::SliderFloat2("DisplayWindowPadding", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.");
ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).");
SeparatorText("Misc");
SliderFloat2("DisplayWindowPadding", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.");
SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).");
ImGui::EndTabItem();
EndTabItem();
}
if (ImGui::BeginTabItem("Colors"))
if (BeginTabItem("Colors"))
{
static int output_dest = 0;
static bool output_only_modified = true;
if (ImGui::Button("Export"))
if (Button("Export"))
{
if (output_dest == 0)
ImGui::LogToClipboard();
LogToClipboard();
else
ImGui::LogToTTY();
ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE);
LogToTTY();
LogText("ImVec4* colors = GetStyle().Colors;" IM_NEWLINE);
for (int i = 0; i < ImGuiCol_COUNT; i++)
{
const ImVec4& col = style.Colors[i];
const char* name = ImGui::GetStyleColorName(i);
const char* name = GetStyleColorName(i);
if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
}
ImGui::LogFinish();
LogFinish();
}
ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified);
SameLine(); SetNextItemWidth(120); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
SameLine(); Checkbox("Only Modified Colors", &output_only_modified);
static ImGuiTextFilter filter;
filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
filter.Draw("Filter colors", GetFontSize() * 16);
static ImGuiColorEditFlags alpha_flags = 0;
if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine();
if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();
if (RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine();
if (RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } SameLine();
if (RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } SameLine();
HelpMarker(
"In the color list:\n"
"Left-click on color square to open color picker,\n"
"Right-click to open edit options menu.");
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));
ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));
BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
PushItemWidth(GetFontSize() * -12);
for (int i = 0; i < ImGuiCol_COUNT; i++)
{
const char* name = ImGui::GetStyleColorName(i);
const char* name = GetStyleColorName(i);
if (!filter.PassFilter(name))
continue;
ImGui::PushID(i);
PushID(i);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (ImGui::Button("?"))
ImGui::DebugFlashStyleColor((ImGuiCol)i);
ImGui::SetItemTooltip("Flash given color to identify places where it is used.");
ImGui::SameLine();
if (Button("?"))
DebugFlashStyleColor((ImGuiCol)i);
SetItemTooltip("Flash given color to identify places where it is used.");
SameLine();
#endif
ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);
ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);
if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)
{
// Tips: in a real user application, you may want to merge and use an icon font into the main font,
// so instead of "Save"/"Revert" you'd use icons!
// Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; }
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; }
SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Save")) { ref->Colors[i] = style.Colors[i]; }
SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Revert")) { style.Colors[i] = ref->Colors[i]; }
}
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
ImGui::TextUnformatted(name);
ImGui::PopID();
SameLine(0.0f, style.ItemInnerSpacing.x);
TextUnformatted(name);
PopID();
}
ImGui::PopItemWidth();
ImGui::EndChild();
PopItemWidth();
EndChild();
ImGui::EndTabItem();
EndTabItem();
}
if (ImGui::BeginTabItem("Fonts"))
if (BeginTabItem("Fonts"))
{
ImGuiIO& io = ImGui::GetIO();
ImGuiIO& io = GetIO();
ImFontAtlas* atlas = io.Fonts;
HelpMarker("Read FAQ and docs/FONTS.md for details on font loading.");
ImGui::ShowFontAtlas(atlas);
ShowFontAtlas(atlas);
// Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
// (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
/*
SeparatorText("Legacy Scaling");
const float MIN_SCALE = 0.3f;
const float MAX_SCALE = 2.0f;
HelpMarker(
@ -8350,120 +8444,121 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
"However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
"Using those settings here will give you poor quality results.");
static float window_scale = 1.0f;
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
ImGui::SetWindowFontScale(window_scale);
ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
ImGui::PopItemWidth();
PushItemWidth(GetFontSize() * 8);
DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
//static float window_scale = 1.0f;
//if (DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
// SetWindowFontScale(window_scale);
PopItemWidth();
*/
ImGui::EndTabItem();
EndTabItem();
}
if (ImGui::BeginTabItem("Rendering"))
if (BeginTabItem("Rendering"))
{
ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
ImGui::SameLine();
Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
SameLine();
HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
ImGui::SameLine();
Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
SameLine();
HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
PushItemWidth(GetFontSize() * 8);
DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
// When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
ImGui::DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp);
const bool show_samples = ImGui::IsItemActive();
DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp);
const bool show_samples = IsItemActive();
if (show_samples)
ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
if (show_samples && ImGui::BeginTooltip())
SetNextWindowPos(GetCursorScreenPos());
if (show_samples && BeginTooltip())
{
ImGui::TextUnformatted("(R = radius, N = approx number of segments)");
ImGui::Spacing();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
const float min_widget_width = ImGui::CalcTextSize("R: MMM\nN: MMM").x;
TextUnformatted("(R = radius, N = approx number of segments)");
Spacing();
ImDrawList* draw_list = GetWindowDrawList();
const float min_widget_width = CalcTextSize("R: MMM\nN: MMM").x;
for (int n = 0; n < 8; n++)
{
const float RAD_MIN = 5.0f;
const float RAD_MAX = 70.0f;
const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
ImGui::BeginGroup();
BeginGroup();
// N is not always exact here due to how PathArcTo() function work internally
ImGui::Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad));
Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad));
const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
const float offset_x = floorf(canvas_width * 0.5f);
const float offset_y = floorf(RAD_MAX);
const ImVec2 p1 = ImGui::GetCursorScreenPos();
draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));
ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
const ImVec2 p1 = GetCursorScreenPos();
draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
Dummy(ImVec2(canvas_width, RAD_MAX * 2));
/*
const ImVec2 p2 = ImGui::GetCursorScreenPos();
draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));
ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
const ImVec2 p2 = GetCursorScreenPos();
draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
Dummy(ImVec2(canvas_width, RAD_MAX * 2));
*/
ImGui::EndGroup();
ImGui::SameLine();
EndGroup();
SameLine();
}
ImGui::EndTooltip();
EndTooltip();
}
ImGui::SameLine();
SameLine();
HelpMarker("When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically.");
ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.
ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha).");
ImGui::PopItemWidth();
DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.
DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha).");
PopItemWidth();
ImGui::EndTabItem();
EndTabItem();
}
ImGui::EndTabBar();
EndTabBar();
}
ImGui::PopItemWidth();
PopItemWidth();
}
//-----------------------------------------------------------------------------
// [SECTION] User Guide / ShowUserGuide()
//-----------------------------------------------------------------------------
// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
void ImGui::ShowUserGuide()
{
ImGuiIO& io = ImGui::GetIO();
ImGui::BulletText("Double-click on title bar to collapse window.");
ImGui::BulletText(
ImGuiIO& io = GetIO();
BulletText("Double-click on title bar to collapse window.");
BulletText(
"Click and drag on lower corner to resize window\n"
"(double-click to auto fit window to its contents).");
ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text.");
ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
ImGui::BulletText("CTRL+Tab to select a window.");
BulletText("CTRL+Click on a slider or drag box to input value as text.");
BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
BulletText("CTRL+Tab to select a window.");
if (io.FontAllowUserScaling)
ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents.");
ImGui::BulletText("While inputting text:\n");
ImGui::Indent();
ImGui::BulletText("CTRL+Left/Right to word jump.");
ImGui::BulletText("CTRL+A or double-click to select all.");
ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
ImGui::BulletText("CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.");
ImGui::BulletText("ESCAPE to revert.");
ImGui::Unindent();
ImGui::BulletText("With keyboard navigation enabled:");
ImGui::Indent();
ImGui::BulletText("Arrow keys to navigate.");
ImGui::BulletText("Space to activate a widget.");
ImGui::BulletText("Return to input text into a widget.");
ImGui::BulletText("Escape to deactivate a widget, close popup, exit child window.");
ImGui::BulletText("Alt to jump to the menu layer of a window.");
ImGui::Unindent();
BulletText("CTRL+Mouse Wheel to zoom window contents.");
BulletText("While inputting text:\n");
Indent();
BulletText("CTRL+Left/Right to word jump.");
BulletText("CTRL+A or double-click to select all.");
BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
BulletText("CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.");
BulletText("ESCAPE to revert.");
Unindent();
BulletText("With keyboard navigation enabled:");
Indent();
BulletText("Arrow keys to navigate.");
BulletText("Space to activate a widget.");
BulletText("Return to input text into a widget.");
BulletText("Escape to deactivate a widget, close popup, exit child window.");
BulletText("Alt to jump to the menu layer of a window.");
Unindent();
}
//-----------------------------------------------------------------------------
@ -9285,8 +9380,10 @@ struct ExampleAppPropertyEditor
ImGui::TableNextColumn();
ImGui::PushID(node->UID);
ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards
tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support
tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;// Standard opening mode as we are likely to want to add selection afterwards
tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support
tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
if (node == VisibleNode)
tree_flags |= ImGuiTreeNodeFlags_Selected;
if (node->Childs.Size == 0)
@ -10368,7 +10465,7 @@ struct ExampleAssetsBrowser
Selection.Clear();
}
// Logic would be written in the main code BeginChild() and outputing to local variables.
// Logic would be written in the main code BeginChild() and outputting to local variables.
// We extracted it into a function so we can call it easily from multiple places.
void UpdateLayoutSizes(float avail_width)
{
@ -10678,9 +10775,8 @@ void ImGui::ShowAboutWindow(bool*) {}
void ImGui::ShowDemoWindow(bool*) {}
void ImGui::ShowUserGuide() {}
void ImGui::ShowStyleEditor(ImGuiStyle*) {}
bool ImGui::ShowStyleSelector(const char* label) { return false; }
void ImGui::ShowFontSelector(const char* label) {}
bool ImGui::ShowStyleSelector(const char*) { return false; }
#endif
#endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS
#endif // #ifndef IMGUI_DISABLE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91b
// dear imgui, v1.92.0 WIP
// (tables and columns code)
/*
@ -451,6 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table
}
// Push a standardized ID for both child-using and not-child-using tables
@ -1510,6 +1511,7 @@ void ImGui::EndTable()
}
else
{
table->InnerWindow->DC.TreeDepth--;
ItemSize(table->OuterRect.GetSize());
ItemAdd(table->OuterRect, 0);
}
@ -1951,7 +1953,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
IM_ASSERT(table->IsInsideRow);
if (table->CurrentColumn != -1)
{
TableEndCell(table);
table->CurrentColumn = -1;
}
// Logging
if (g.LogEnabled)
@ -2191,6 +2196,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
g.LastItemData.StatusFlags = 0;
}
// Also see TablePushColumnChannel()
if (table->Flags & ImGuiTableFlags_NoClip)
{
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
@ -2464,10 +2470,38 @@ void ImGui::TablePopBackgroundChannel()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable;
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
// Optimization: avoid PopClipRect() + SetCurrentChannel()
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
}
// Also see TableBeginCell()
void ImGui::TablePushColumnChannel(int column_n)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid SetCurrentChannel() + PushClipRect()
if (table->Flags & ImGuiTableFlags_NoClip)
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[column_n];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
void ImGui::TablePopColumnChannel()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid PopClipRect() + SetCurrentChannel()
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
@ -3244,7 +3278,7 @@ void ImGui::TableHeader(const char* label)
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
// be merged into a single draw call.
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
if (text_clipped && hovered && g.ActiveId == 0)
@ -3341,7 +3375,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id);
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
@ -3396,7 +3430,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
PushStyleColor(ImGuiCol_Text, request->TextColor);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
PopStyleColor();
int vtx_idx_end = draw_list->_VtxCurrentIdx;

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91b
// dear imgui, v1.92.0 WIP
// (widgets code)
/*
@ -339,6 +339,46 @@ void ImGui::TextWrappedV(const char* fmt, va_list args)
PopTextWrapPos();
}
void ImGui::TextAligned(float align_x, float size_x, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
TextAlignedV(align_x, size_x, fmt, args);
va_end(args);
}
// align_x: 0.0f = left, 0.5f = center, 1.0f = right.
// size_x : 0.0f = shortcut for GetContentRegionAvail().x
// FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
void ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
const char* text, *text_end;
ImFormatStringToTempBufferV(&text, &text_end, fmt, args);
const ImVec2 text_size = CalcTextSize(text, text_end);
size_x = CalcItemSize(ImVec2(size_x, 0.0f), 0.0f, text_size.y).x;
ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
ImVec2 pos_max(pos.x + size_x, window->ClipRect.Max.y);
ImVec2 size(ImMin(size_x, text_size.x), text_size.y);
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, pos.x + text_size.x);
window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, pos.x + text_size.x);
if (align_x > 0.0f && text_size.x < size_x)
pos.x += ImTrunc((size_x - text_size.x) * align_x);
RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size);
const ImVec2 backup_max_pos = window->DC.CursorMaxPos;
ItemSize(size);
ItemAdd(ImRect(pos, pos + size), 0);
window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content size because right-aligned text would otherwise mess it up.
if (size_x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip))
SetTooltip("%.*s", (int)(text_end - text), text);
}
void ImGui::LabelText(const char* label, const char* fmt, ...)
{
va_list args;
@ -494,7 +534,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature.
// One idiom which was previously valid which will now emit a warning is when using multiple overlayed ButtonBehavior()
// One idiom which was previously valid which will now emit a warning is when using multiple overlaid ButtonBehavior()
// with same ID and different MouseButton (see #8030). You can fix it by:
// (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags.
// or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
@ -518,7 +558,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_;
ImGuiWindow* backup_hovered_window = g.HoveredWindow;
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window->RootWindow;
if (flatten_hovered_children)
g.HoveredWindow = window;
@ -867,11 +907,12 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
if (hovered)
window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
ImU32 cross_col = GetColorU32(ImGuiCol_Text);
ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f);
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f);
const ImU32 cross_col = GetColorU32(ImGuiCol_Text);
const ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
const float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
const float cross_thickness = 1.0f; // FIXME-DPI
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, cross_thickness);
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, cross_thickness);
return pressed;
}
@ -1063,9 +1104,9 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
return held;
}
// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
// - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
void ImGui::ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@ -1083,28 +1124,28 @@ void ImGui::ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, c
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, ImDrawFlags_None, g.Style.ImageBorderSize);
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
}
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
{
ImageWithBg(user_texture_id, image_size, uv0, uv1);
ImageWithBg(tex_ref, image_size, uv0, uv1);
}
// 1.91.9 (February 2025) removed 'tint_col' and 'border_col' parameters, made border size not depend on color value. (#8131, #8238)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiContext& g = *GImGui;
PushStyleVar(ImGuiStyleVar_ImageBorderSize, (border_col.w > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); // Preserve legacy behavior where border is always visible when border_col's Alpha is >0.0f
PushStyleColor(ImGuiCol_Border, border_col);
ImageWithBg(user_texture_id, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
ImageWithBg(tex_ref, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
PopStyleColor();
PopStyleVar();
}
#endif
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@ -1126,21 +1167,21 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2&
RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
return pressed;
}
// - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) // FIXME: Maybe that's not the best design?
bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
bool ImGui::ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col);
return ImageButtonEx(window->GetID(str_id), tex_ref, image_size, uv0, uv1, bg_col, tint_col);
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@ -1478,8 +1519,8 @@ bool ImGui::TextLink(const char* label)
ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z);
}
float line_y = bb.Max.y + ImFloor(g.Font->Descent * g.FontScale * 0.20f);
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode.
float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f);
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode // FIXME-DPI
PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));
RenderText(bb.Min, label, label_end);
@ -1489,14 +1530,14 @@ bool ImGui::TextLink(const char* label)
return pressed;
}
void ImGui::TextLinkOpenURL(const char* label, const char* url)
bool ImGui::TextLinkOpenURL(const char* label, const char* url)
{
ImGuiContext& g = *GImGui;
if (url == NULL)
url = label;
if (TextLink(label))
if (g.PlatformIO.Platform_OpenInShellFn != NULL)
g.PlatformIO.Platform_OpenInShellFn(&g, url);
bool pressed = TextLink(label);
if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)
g.PlatformIO.Platform_OpenInShellFn(&g, url);
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label
if (BeginPopupContextItem())
{
@ -1504,6 +1545,7 @@ void ImGui::TextLinkOpenURL(const char* label, const char* url)
SetClipboardText(url);
EndPopup();
}
return pressed;
}
//-------------------------------------------------------------------------
@ -1690,7 +1732,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
if (g.LogEnabled)
LogSetNextTextDecoration("---", NULL);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size);
}
else
{
@ -3891,7 +3933,7 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si
return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
}
// This is only used in the path where the multiline widget is inactivate.
// This is only used in the path where the multiline widget is inactive.
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
{
int line_count = 0;
@ -3915,9 +3957,10 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char**
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
{
ImGuiContext& g = *ctx;
ImFont* font = g.Font;
//ImFont* font = g.Font;
ImFontBaked* baked = g.FontBaked;
const float line_height = g.FontSize;
const float scale = line_height / font->FontSize;
const float scale = line_height / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
@ -3943,8 +3986,7 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
if (c == '\r')
continue;
const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale;
line_width += char_width;
line_width += baked->GetCharAdvance((ImWchar)c) * scale;
}
if (text_size.x < line_width)
@ -3971,7 +4013,7 @@ namespace ImStb
{
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; }
static char STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
{
@ -4273,18 +4315,29 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
void ImGui::PushPasswordFont()
{
ImGuiContext& g = *GImGui;
ImFont* in_font = g.Font;
ImFont* out_font = &g.InputTextPasswordFont;
ImFontGlyph* glyph = in_font->FindGlyph('*');
out_font->FontSize = in_font->FontSize;
out_font->Scale = in_font->Scale;
out_font->Ascent = in_font->Ascent;
out_font->Descent = in_font->Descent;
out_font->ContainerAtlas = in_font->ContainerAtlas;
out_font->FallbackGlyph = glyph;
out_font->FallbackAdvanceX = glyph->AdvanceX;
IM_ASSERT(out_font->Glyphs.Size == 0 && out_font->IndexAdvanceX.Size == 0 && out_font->IndexLookup.Size == 0);
PushFont(out_font);
ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
ImFontGlyph* glyph = g.FontBaked->FindGlyph('*');
g.InputTextPasswordFontBackupFlags = g.Font->Flags;
backup->FallbackGlyphIndex = g.FontBaked->FallbackGlyphIndex;
backup->FallbackAdvanceX = g.FontBaked->FallbackAdvanceX;
backup->IndexLookup.swap(g.FontBaked->IndexLookup);
backup->IndexAdvanceX.swap(g.FontBaked->IndexAdvanceX);
g.Font->Flags |= ImFontFlags_NoLoadGlyphs;
g.FontBaked->FallbackGlyphIndex = g.FontBaked->Glyphs.index_from_ptr(glyph);
g.FontBaked->FallbackAdvanceX = glyph->AdvanceX;
}
void ImGui::PopPasswordFont()
{
ImGuiContext& g = *GImGui;
ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
g.Font->Flags = g.InputTextPasswordFontBackupFlags;
g.FontBaked->FallbackGlyphIndex = backup->FallbackGlyphIndex;
g.FontBaked->FallbackAdvanceX = backup->FallbackAdvanceX;
g.FontBaked->IndexLookup.swap(backup->IndexLookup);
g.FontBaked->IndexAdvanceX.swap(backup->IndexAdvanceX);
IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
}
// Return false to discard a character.
@ -4648,7 +4701,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (g.ActiveId == id)
{
// Declare some inputs, the other are registered and polled via Shortcut() routing system.
// FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combinaison into individual shortcuts.
// FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts.
const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End };
for (ImGuiKey key : always_owned_keys)
SetKeyOwner(key, id);
@ -5161,8 +5214,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Otherwise request text input ahead for next frame.
if (g.ActiveId == id && clear_active_id)
ClearActiveID();
else if (g.ActiveId == id)
g.WantTextInputNextFrame = 1;
// Render frame
if (!is_multiline)
@ -5188,7 +5239,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (new_is_displaying_hint != is_displaying_hint)
{
if (is_password && !is_displaying_hint)
PopFont();
PopPasswordFont();
is_displaying_hint = new_is_displaying_hint;
if (is_password && !is_displaying_hint)
PushPasswordFont();
@ -5313,7 +5364,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
else
{
ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true);
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
@ -5329,7 +5380,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
{
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
draw_window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
}
// Draw blinking cursor
@ -5340,14 +5391,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
if (!is_readonly)
// This is required for some backends (SDL3) to start emitting character/text inputs.
// As per #6341, make sure we don't set that on the deactivating frame.
if (!is_readonly && g.ActiveId == id)
{
g.PlatformImeData.WantVisible = true;
g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
g.PlatformImeData.InputLineHeight = g.FontSize;
ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
ime_data->WantVisible = true;
ime_data->WantTextInput = true;
ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
ime_data->InputLineHeight = g.FontSize;
ime_data->ViewportId = window->Viewport->ID;
}
}
}
@ -5369,12 +5425,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive?
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
draw_window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
}
}
if (is_password && !is_displaying_hint)
PopFont();
PopPasswordFont();
if (is_multiline)
{
@ -6208,7 +6264,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
if (g.Style.FrameBorderSize > 0.0f)
RenderFrameBorder(bb.Min, bb.Max, rounding);
else
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border // FIXME-DPI
}
// Drag and Drop Source
@ -6390,6 +6446,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
// - TreeNodeV()
// - TreeNodeEx()
// - TreeNodeExV()
// - TreeNodeStoreStackData() [Internal]
// - TreeNodeBehavior() [Internal]
// - TreePush()
// - TreePop()
@ -6548,18 +6605,26 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags)
// Store ImGuiTreeNodeStackData for just submitted node.
// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase.
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags)
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1);
ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back();
ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
tree_node_data->ID = g.LastItemData.ID;
tree_node_data->TreeFlags = flags;
tree_node_data->ItemFlags = g.LastItemData.ItemFlags;
tree_node_data->NavRect = g.LastItemData.NavRect;
// Initially I tried to latch value for GetColorU32(ImGuiCol_TreeLines) but it's not a good trade-off for very large trees.
const bool draw_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) != 0;
tree_node_data->DrawLinesX1 = draw_lines ? (x1 + g.FontSize * 0.5f + g.Style.FramePadding.x) : +FLT_MAX;
tree_node_data->DrawLinesTableColumn = (draw_lines && g.CurrentTable) ? (ImGuiTableColumnIdx)g.CurrentTable->CurrentColumn : -1;
tree_node_data->DrawLinesToNodesY2 = -FLT_MAX;
window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);
if (flags & ImGuiTreeNodeFlags_DrawLinesToNodes)
window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth);
}
// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop.
@ -6629,14 +6694,18 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
g.LastItemData.DisplayRect = frame_bb;
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled:
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsToParent enabled:
// Store data for the current depth to allow returning to this node from any child item.
// For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
// It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle.
// It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsToParent by default or move it to ImGuiStyle.
bool store_tree_node_stack_data = false;
if ((flags & ImGuiTreeNodeFlags_DrawLinesMask_) == 0)
flags |= g.Style.TreeLinesFlags;
const bool draw_tree_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) && (frame_bb.Min.y < window->ClipRect.Max.y) && (g.Style.TreeLinesSize > 0.0f);
if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
{
if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive)
store_tree_node_stack_data = draw_tree_lines;
if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsToParent) && !g.NavIdIsAlive)
if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
store_tree_node_stack_data = true;
}
@ -6644,8 +6713,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
if (!is_visible)
{
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
if ((flags & ImGuiTreeNodeFlags_DrawLinesToNodes) && (window->DC.TreeRecordsClippedNodesY2Mask & (1 << (window->DC.TreeDepth - 1))))
{
ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, window->DC.CursorPos.y); // Don't need to aim to mid Y position as we are clipped anyway.
if (frame_bb.Min.y >= window->ClipRect.Max.y)
window->DC.TreeRecordsClippedNodesY2Mask &= ~(1 << (window->DC.TreeDepth - 1)); // Done
}
if (is_open && store_tree_node_stack_data)
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushOverrideID(id);
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
@ -6691,6 +6767,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
else
button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
if (flags & ImGuiTreeNodeFlags_NoNavFocus)
button_flags |= ImGuiButtonFlags_NoNavFocus;
bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
const bool was_selected = selected;
@ -6777,6 +6855,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
@ -6797,6 +6877,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
}
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
@ -6805,8 +6887,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
LogSetNextTextDecoration(">", NULL);
}
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (draw_tree_lines)
TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.5f));
// Label
if (display_frame)
@ -6818,8 +6900,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
TablePopBackgroundChannel();
}
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
if (is_open && store_tree_node_stack_data)
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice
@ -6827,6 +6909,64 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
return is_open;
}
// Draw horizontal line from our parent node
// This is only called for visible child nodes so we are not too fussy anymore about performances
void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->DC.TreeDepth == 0 || (window->DC.TreeHasStackDataDepthMask & (1 << (window->DC.TreeDepth - 1))) == 0)
return;
ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
float x1 = ImTrunc(parent_data->DrawLinesX1);
float x2 = ImTrunc(target_pos.x - g.Style.ItemInnerSpacing.x);
float y = ImTrunc(target_pos.y);
float rounding = (g.Style.TreeLinesRounding > 0.0f) ? ImMin(x2 - x1, g.Style.TreeLinesRounding) : 0.0f;
parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, y - rounding);
if (x1 >= x2)
return;
if (rounding > 0.0f)
{
x1 += 0.5f + rounding;
window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3);
if (x1 < x2)
window->DrawList->PathLineTo(ImVec2(x2, y));
window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize);
}
else
{
window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
}
}
// Draw vertical line of the hierarchy
void ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
float y1 = ImMax(data->NavRect.Max.y, window->ClipRect.Min.y);
float y2 = data->DrawLinesToNodesY2;
if (data->TreeFlags & ImGuiTreeNodeFlags_DrawLinesFull)
{
float y2_full = window->DC.CursorPos.y;
if (g.CurrentTable)
y2_full = ImMax(g.CurrentTable->RowPosY2, y2_full);
y2_full = ImTrunc(y2_full - g.Style.ItemSpacing.y - g.FontSize * 0.5f);
if (y2 + (g.Style.ItemSpacing.y + g.Style.TreeLinesRounding) < y2_full) // FIXME: threshold to use ToNodes Y2 instead of Full Y2 when close by ItemSpacing.y
y2 = y2_full;
}
y2 = ImMin(y2, window->ClipRect.Max.y);
if (y2 <= y1)
return;
float x = ImTrunc(data->DrawLinesX1);
if (data->DrawLinesTableColumn != -1)
TablePushColumnChannel(data->DrawLinesTableColumn);
window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
if (data->DrawLinesTableColumn != -1)
TablePopColumnChannel();
}
void ImGui::TreePush(const char* str_id)
{
ImGuiWindow* window = GetCurrentWindow();
@ -6861,18 +7001,23 @@ void ImGui::TreePop()
window->DC.TreeDepth--;
ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);
if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask) // Only set during request
if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask)
{
ImGuiTreeNodeStackData* data = &g.TreeNodeStack.back();
const ImGuiTreeNodeStackData* data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
IM_ASSERT(data->ID == window->IDStack.back());
if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere)
{
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled)
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsToParent is enabled)
if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsToParent)
if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data);
}
// Draw hierarchy lines
if (data->DrawLinesX1 != +FLT_MAX && window->DC.CursorPos.y >= window->ClipRect.Min.y)
TreeNodeDrawLineToTreePop(data);
g.TreeNodeStack.pop_back();
window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask;
window->DC.TreeRecordsClippedNodesY2Mask &= ~tree_depth_mask;
}
IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
@ -7487,7 +7632,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
ImRect box_select_r = bs->BoxSelectRectCurr;
box_select_r.ClipWith(scope_rect);
window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT: Styling
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling
// Scroll
const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;
@ -7691,7 +7836,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
if (ms->IsFocused)
{
// We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here.
if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure)
if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at beginning of the scope (see tests for easy failure)
{
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId.
storage->RangeSrcItem = ImGuiSelectionUserData_Invalid;
@ -10377,13 +10522,12 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
#endif
// Render text label (with clipping + alpha gradient) + unsaved marker
ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;
ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
// Return clipped state ignoring the close button
if (out_text_clipped)
{
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x;
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x;
//draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
}
@ -10429,15 +10573,22 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
// This is all rather complicated
// (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)
// FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..
float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f;
float ellipsis_max_x = text_ellipsis_clip_bb.Max.x;
if (close_button_visible || unsaved_marker_visible)
{
text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f);
text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f;
ellipsis_max_x = text_pixel_clip_bb.Max.x;
const bool visible_without_hover = unsaved_marker_visible || (is_contents_visible ? g.Style.TabCloseButtonMinWidthSelected : g.Style.TabCloseButtonMinWidthUnselected) < 0.0f;
if (visible_without_hover)
{
text_ellipsis_clip_bb.Max.x -= button_sz * 0.90f;
ellipsis_max_x -= button_sz * 0.90f;
}
else
{
text_ellipsis_clip_bb.Max.x -= button_sz * 1.00f;
}
}
LogSetNextTextDecoration("/", "\\");
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size);
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size);
#if 0
if (!is_contents_visible)

View File

@ -1,104 +0,0 @@
#!/usr/bin/env python3
import code
import sys
import os
import glob
import re
#src_file = "src/duckstation-qt/qttranslations.cpp"
src_dir = os.path.join(os.path.dirname(__file__), "..", "src")
fa_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsFontAwesome5.h")
pf_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsPromptFont.h")
emoji_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsEmoji.h")
dst_file = os.path.join(os.path.dirname(__file__), "..", "src", "util", "imgui_glyph_ranges.inl")
all_source_files = glob.glob(os.path.join(src_dir, "**", "*.cpp"), recursive=True) + \
glob.glob(os.path.join(src_dir, "**", "*.h"), recursive=True) + \
glob.glob(os.path.join(src_dir, "**", "*.inl"), recursive=True)
tokens = set()
pf_tokens = set()
emoji_tokens = set()
for filename in all_source_files:
data = None
with open(filename, "r") as f:
try:
data = f.read()
except:
continue
tokens = tokens.union(set(re.findall("(ICON_FA_[a-zA-Z0-9_]+)", data)))
pf_tokens = pf_tokens.union(set(re.findall("(ICON_PF_[a-zA-Z0-9_]+)", data)))
emoji_tokens = emoji_tokens.union(set(re.findall("(ICON_EMOJI_[a-zA-Z0-9_]+)", data)))
print("{}/{}/{} tokens found.".format(len(tokens), len(pf_tokens), len(emoji_tokens)))
if len(tokens) == 0 and len(pf_tokens) == 0:
sys.exit(0)
u8_encodings = {}
with open(fa_file, "r") as f:
for line in f.readlines():
match = re.match("#define (ICON_FA_[^ ]+) \"([^\"]+)\"", line)
if match is None:
continue
u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", ""))
with open(pf_file, "r") as f:
for line in f.readlines():
match = re.match("#define (ICON_PF_[^ ]+) \"([^\"]+)\"", line)
if match is None:
continue
u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", ""))
with open(emoji_file, "r") as f:
for line in f.readlines():
match = re.match("#define (ICON_EMOJI_[^ ]+) \"([^\"]+)\"", line)
if match is None:
continue
u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", ""))
out_pattern = "(static constexpr ImWchar FA_ICON_RANGE\\[\\] = \\{)[0-9A-Z_a-z, \n]+(\\};)"
out_pf_pattern = "(static constexpr ImWchar PF_ICON_RANGE\\[\\] = \\{)[0-9A-Z_a-z, \n]+(\\};)"
out_emoji_pattern = "(static constexpr ImWchar EMOJI_ICON_RANGE\\[\\] = \\{)[0-9A-Z_a-z, \n]+(\\};)"
def get_pairs(tokens):
codepoints = list()
for token in tokens:
u8_bytes = u8_encodings[token]
u8 = str(u8_bytes, "utf-8")
u32 = u8.encode("utf-32le")
if len(u32) > 4:
raise ValueError("{} {} too long".format(u8_bytes, token))
codepoint = int.from_bytes(u32, byteorder="little", signed=False)
codepoints.append(codepoint)
codepoints.sort()
codepoints.append(0) # null terminator
startc = codepoints[0]
endc = None
pairs = [startc]
for codepoint in codepoints:
if endc is not None and (endc + 1) != codepoint:
pairs.append(endc)
pairs.append(codepoint)
startc = codepoint
endc = codepoint
else:
endc = codepoint
pairs.append(endc)
pairs_str = ",".join(list(map(lambda x: "0x{:x}".format(x), pairs)))
return pairs_str
with open(dst_file, "r") as f:
original = f.read()
updated = re.sub(out_pattern, "\\1 " + get_pairs(tokens) + " \\2", original)
updated = re.sub(out_pf_pattern, "\\1 " + get_pairs(pf_tokens) + " \\2", updated)
updated = re.sub(out_emoji_pattern, "\\1 " + get_pairs(emoji_tokens) + " \\2", updated)
if original != updated:
with open(dst_file, "w") as f:
f.write(updated)
print("Updated {}".format(dst_file))
else:
print("Skipping updating {}".format(dst_file))

View File

@ -1,68 +0,0 @@
#!/usr/bin/env python3
import os
import re
import xml.etree.ElementTree as ET
languages_to_update = [
"ja",
"ko",
"zh-cn",
]
src_path = os.path.join(os.path.dirname(__file__), "..", "src", "duckstation-qt", "qttranslations.cpp")
ts_dir = os.path.join(os.path.dirname(__file__), "..", "src", "duckstation-qt", "translations")
def parse_xml(path):
tree = ET.parse(path)
root = tree.getroot()
translations = ""
for node in root.findall("context/message/translation"):
if node.text:
translations += node.text
ords = list(set([ord(ch) for ch in translations if ord(ch) >= 0x2000]))
if len(ords) == 0:
return ""
# Try to organize it into ranges
ords.sort()
ord_pairs = []
start_ord = None
last_ord = None
for nord in ords:
if start_ord is not None and nord == (last_ord + 1):
last_ord = nord
continue
if start_ord is not None:
ord_pairs.append(start_ord)
ord_pairs.append(last_ord)
start_ord = nord
last_ord = nord
if start_ord is not None:
ord_pairs.append(start_ord)
ord_pairs.append(last_ord)
chars = "".join([chr(ch) for ch in ord_pairs])
return chars
def update_src_file(ts_file, chars):
ts_name = os.path.basename(ts_file)
pattern = re.compile('(// auto update.*' + ts_name + '.*\n[^"]+")[^"]*(".*)')
with open(src_path, "r", encoding="utf-8") as f:
original = f.read()
update = pattern.sub("\\1" + chars + "\\2", original)
if original != update:
with open(src_path, "w", encoding="utf-8") as f:
f.write(update)
print(f"Updated character list for {ts_file}.")
else:
print(f"Character list is unchanged for {ts_file}.")
if __name__ == "__main__":
for language in languages_to_update:
ts_file = os.path.join(ts_dir, f"duckstation-qt_{language}.ts")
chars = parse_xml(ts_file)
print(f"{language}: {len(chars)} character(s) detected.")
update_src_file(ts_file, chars)

View File

@ -153,7 +153,6 @@ static std::string GetImageURL(const char* image_name, u32 type);
static std::string GetLocalImagePath(const std::string_view image_name, u32 type);
static void DownloadImage(std::string url, std::string cache_path);
static const std::string& GetCachedAchievementBadgePath(const rc_client_achievement_t* achievement, bool locked);
static void UpdateGlyphRanges();
static TinyString DecryptLoginToken(std::string_view encrypted_token, std::string_view username);
static TinyString EncryptLoginToken(std::string_view token, std::string_view username);
@ -479,114 +478,6 @@ void Achievements::DownloadImage(std::string url, std::string cache_path)
s_state.http_downloader->CreateRequest(std::move(url), std::move(callback));
}
void Achievements::UpdateGlyphRanges()
{
// To avoid rasterizing all emoji fonts, we get the set of used glyphs in the emoji range for all strings in the
// current game's achievement data.
using CodepointSet = std::unordered_set<ImGuiManager::WCharType>;
CodepointSet codepoints, emoji_codepoints;
const auto add_string = [&codepoints, &emoji_codepoints](const std::string_view str) {
char32_t codepoint;
for (size_t offset = 0; offset < str.length();)
{
offset += StringUtil::DecodeUTF8(str, offset, &codepoint);
// Basic Latin + Latin Supplement always included.
if (codepoint != StringUtil::UNICODE_REPLACEMENT_CHARACTER && codepoint >= 0x100)
{
CodepointSet& dest = (codepoint >= 0x2000) ? emoji_codepoints : codepoints;
dest.insert(static_cast<ImGuiManager::WCharType>(codepoint));
}
}
};
#ifndef __ANDROID__
// We don't need to check rich presence on Android, because we're not displaying it with FullscreenUI.
if (rc_client_has_rich_presence(s_state.client))
{
std::vector<const char*> rp_strings;
for (;;)
{
rp_strings.resize(std::max<size_t>(rp_strings.size() * 2, 512));
size_t count;
const int err = rc_client_get_rich_presence_strings(s_state.client, rp_strings.data(), rp_strings.size(), &count);
if (err == RC_INSUFFICIENT_BUFFER)
continue;
else if (err != RC_OK)
rp_strings.clear();
else
rp_strings.resize(count);
break;
}
for (const char* str : rp_strings)
add_string(str);
}
#endif
if (rc_client_has_achievements(s_state.client))
{
rc_client_achievement_list_t* const achievements =
rc_client_create_achievement_list(s_state.client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL, 0);
if (achievements)
{
for (u32 i = 0; i < achievements->num_buckets; i++)
{
const rc_client_achievement_bucket_t& bucket = achievements->buckets[i];
for (u32 j = 0; j < bucket.num_achievements; j++)
{
const rc_client_achievement_t* achievement = bucket.achievements[j];
if (achievement->title)
add_string(achievement->title);
if (achievement->description)
add_string(achievement->description);
}
}
rc_client_destroy_achievement_list(achievements);
}
}
if (rc_client_has_leaderboards(s_state.client, false))
{
rc_client_leaderboard_list_t* const leaderboards =
rc_client_create_leaderboard_list(s_state.client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
if (leaderboards)
{
for (u32 i = 0; i < leaderboards->num_buckets; i++)
{
const rc_client_leaderboard_bucket_t& bucket = leaderboards->buckets[i];
for (u32 j = 0; j < bucket.num_leaderboards; j++)
{
const rc_client_leaderboard_t* leaderboard = bucket.leaderboards[j];
if (leaderboard->title)
add_string(leaderboard->title);
if (leaderboard->description)
add_string(leaderboard->description);
}
}
rc_client_destroy_leaderboard_list(leaderboards);
}
}
std::vector<ImGuiManager::WCharType> sorted_codepoints, sorted_emoji_codepoints;
sorted_codepoints.reserve(codepoints.size());
sorted_codepoints.insert(sorted_codepoints.begin(), codepoints.begin(), codepoints.end());
std::sort(sorted_codepoints.begin(), sorted_codepoints.end());
sorted_emoji_codepoints.reserve(codepoints.size());
sorted_emoji_codepoints.insert(sorted_emoji_codepoints.begin(), emoji_codepoints.begin(), emoji_codepoints.end());
std::sort(sorted_emoji_codepoints.begin(), sorted_emoji_codepoints.end());
// Compact codepoints to ranges.
GPUThread::RunOnThread(
[sorted_codepoints = std::move(sorted_codepoints), sorted_emoji_codepoints = std::move(sorted_emoji_codepoints)]() {
ImGuiManager::SetDynamicFontRange(ImGuiManager::CompactFontRange(sorted_codepoints),
ImGuiManager::CompactFontRange(sorted_emoji_codepoints));
});
}
bool Achievements::IsActive()
{
return (s_state.client != nullptr);
@ -853,7 +744,6 @@ void Achievements::Shutdown()
ClearGameInfo();
ClearGameHash();
DisableHardcoreMode(false, false);
UpdateGlyphRanges();
CancelHashDatabaseRequests();
if (s_state.login_request)
@ -1206,7 +1096,6 @@ void Achievements::OnSystemDestroyed()
ClearGameInfo();
ClearGameHash();
DisableHardcoreMode(false, false);
UpdateGlyphRanges();
}
void Achievements::OnSystemReset()
@ -1337,10 +1226,7 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
// Unknown game.
INFO_LOG("Unknown game '{}', disabling achievements.", GameHashToString(s_state.game_hash.value()));
if (was_disc_change)
{
ClearGameInfo();
UpdateGlyphRanges();
}
DisableHardcoreMode(false, false);
return;
@ -1369,10 +1255,7 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
{
ReportFmtError("Loading game failed: {}", error_message);
if (was_disc_change)
{
ClearGameInfo();
UpdateGlyphRanges();
}
DisableHardcoreMode(false, false);
return;
@ -1383,10 +1266,7 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
{
ReportError("rc_client_get_game_info() returned NULL");
if (was_disc_change)
{
ClearGameInfo();
UpdateGlyphRanges();
}
DisableHardcoreMode(false, false);
return;
@ -1409,9 +1289,6 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
s_state.has_leaderboards = has_leaderboards;
s_state.has_rich_presence = rc_client_has_rich_presence(client);
// update ranges before initializing fsui
UpdateGlyphRanges();
// ensure fullscreen UI is ready for notifications
if (display_summary)
GPUThread::RunOnThread(&FullscreenUI::Initialize);
@ -2322,7 +2199,6 @@ void Achievements::Logout()
if (HasActiveGame())
{
ClearGameInfo();
UpdateGlyphRanges();
DisableHardcoreMode(false, false);
}
@ -2483,8 +2359,8 @@ void Achievements::DrawGameOverlays()
const float opacity = IndicatorOpacity(io.DeltaTime, indicator);
const std::string_view text = s_state.active_progress_indicator->achievement->measured_progress;
const ImVec2 text_size =
UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(text));
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
0.0f, IMSTR_START_END(text));
const ImVec2 box_min = ImVec2(position.x - image_size.x - text_size.x - spacing - padding * 2.0f,
position.y - image_size.y - padding * 2.0f);
@ -2504,7 +2380,7 @@ void Achievements::DrawGameOverlays()
const ImVec2 text_pos =
box_min + ImVec2(padding + image_size.x + spacing, (box_max.y - box_min.y - text_size.y) * 0.5f);
const ImRect text_clip_rect(text_pos, box_max);
RenderShadowedTextClipped(dl, UIStyle.MediumFont, text_pos, box_max,
RenderShadowedTextClipped(dl, UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, text_pos, box_max,
ImGui::GetColorU32(ModAlpha(UIStyle.ToastTextColor, opacity)), text, &text_size,
ImVec2(0.0f, 0.0f), 0.0f, &text_clip_rect);
@ -2528,23 +2404,23 @@ void Achievements::DrawGameOverlays()
width_string.append(ICON_FA_STOPWATCH);
for (u32 i = 0; i < indicator.text.length(); i++)
width_string.append('0');
const ImVec2 size = ImGuiFullscreen::UIStyle.MediumFont->CalcTextSizeA(
ImGuiFullscreen::UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(width_string));
const ImVec2 size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX, 0.0f,
IMSTR_START_END(width_string));
const ImRect box(ImVec2(position.x - size.x - padding * 2.0f, position.y - size.y - padding * 2.0f), position);
dl->AddRectFilled(box.Min, box.Max,
ImGui::GetColorU32(ModAlpha(UIStyle.ToastBackgroundColor, opacity * bg_opacity)), rounding);
const u32 text_col = ImGui::GetColorU32(ModAlpha(UIStyle.ToastTextColor, opacity));
const ImVec2 text_size = ImGuiFullscreen::UIStyle.MediumFont->CalcTextSizeA(
ImGuiFullscreen::UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(indicator.text));
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
0.0f, IMSTR_START_END(indicator.text));
const ImVec2 text_pos = ImVec2(box.Max.x - padding - text_size.x, box.Min.y + padding);
RenderShadowedTextClipped(dl, UIStyle.MediumFont, text_pos, box.Max, text_col, indicator.text, &text_size,
ImVec2(0.0f, 0.0f), 0.0f, &box);
RenderShadowedTextClipped(dl, UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, text_pos, box.Max,
text_col, indicator.text, &text_size, ImVec2(0.0f, 0.0f), 0.0f, &box);
const ImVec2 icon_pos = ImVec2(box.Min.x + padding, box.Min.y + padding);
RenderShadowedTextClipped(dl, UIStyle.MediumFont, icon_pos, box.Max, text_col, ICON_FA_STOPWATCH, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &box);
RenderShadowedTextClipped(dl, UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, icon_pos, box.Max,
text_col, ICON_FA_STOPWATCH, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &box);
if (!indicator.active && opacity <= 0.01f)
{
@ -2593,7 +2469,7 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
const float progress_height = LayoutScale(20.0f);
const float progress_rounding = LayoutScale(5.0f);
const float badge_size = LayoutScale(40.0f);
const float badge_text_width = box_content_width - badge_size - text_spacing - text_spacing;
const float badge_text_width = box_content_width - badge_size - (text_spacing * 3.0f);
const bool disconnected = rc_client_is_disconnected(s_state.client);
const int pending_count = disconnected ? rc_client_get_award_achievement_pending_count(s_state.client) : 0;
@ -2601,29 +2477,29 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
const auto get_achievement_height = [&badge_size, &badge_text_width, &text_spacing](std::string_view description,
bool show_measured) {
const ImVec2 description_size = description.empty() ?
ImVec2(0.0f, 0.0f) :
UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX,
badge_text_width, IMSTR_START_END(description));
const float text_height = UIStyle.MediumFont->FontSize + text_spacing + description_size.y;
const ImVec2 description_size =
description.empty() ? ImVec2(0.0f, 0.0f) :
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
badge_text_width, IMSTR_START_END(description));
const float text_height = UIStyle.MediumFontSize + text_spacing + description_size.y;
return std::max(text_height, badge_size);
};
float box_height =
box_padding + box_padding + UIStyle.MediumFont->FontSize + paragraph_spacing + progress_height + paragraph_spacing;
box_padding + box_padding + UIStyle.MediumFontSize + paragraph_spacing + progress_height + paragraph_spacing;
if (pending_count > 0)
{
box_height += UIStyle.MediumFont->FontSize + paragraph_spacing;
box_height += UIStyle.MediumFontSize + paragraph_spacing;
}
if (s_state.most_recent_unlock.has_value())
{
box_height += UIStyle.MediumFont->FontSize + paragraph_spacing +
box_height += UIStyle.MediumFontSize + paragraph_spacing +
get_achievement_height(s_state.most_recent_unlock->description, false) +
(s_state.achievement_nearest_completion ? (paragraph_spacing + paragraph_spacing) : 0.0f);
}
if (s_state.achievement_nearest_completion.has_value())
{
box_height += UIStyle.MediumFont->FontSize + paragraph_spacing +
box_height += UIStyle.MediumFontSize + paragraph_spacing +
get_achievement_height(s_state.achievement_nearest_completion->description, true);
}
@ -2639,26 +2515,26 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
&badge_size](std::string_view title, std::string_view description,
const std::string& badge_path, bool show_measured) {
const ImVec2 image_max = ImVec2(text_pos.x + badge_size, text_pos.y + badge_size);
ImVec2 badge_text_pos = ImVec2(image_max.x + text_spacing + text_spacing, text_pos.y);
ImVec2 badge_text_pos = ImVec2(image_max.x + (text_spacing * 3.0f), text_pos.y);
const ImVec4 clip_rect = ImVec4(badge_text_pos.x, badge_text_pos.y, badge_text_pos.x + badge_text_width, box_max.y);
const ImVec2 description_size = description.empty() ?
ImVec2(0.0f, 0.0f) :
UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX,
badge_text_width, IMSTR_START_END(description));
const ImVec2 description_size =
description.empty() ? ImVec2(0.0f, 0.0f) :
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
badge_text_width, IMSTR_START_END(description));
GPUTexture* badge_tex = ImGuiFullscreen::GetCachedTextureAsync(badge_path);
dl->AddImage(badge_tex, text_pos, image_max);
if (!title.empty())
{
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, badge_text_pos, title_text_color,
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, badge_text_pos, title_text_color,
IMSTR_START_END(title), 0.0f, &clip_rect);
badge_text_pos.y += UIStyle.MediumFont->FontSize + text_spacing;
badge_text_pos.y += UIStyle.MediumFontSize + text_spacing;
}
if (!description.empty())
{
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, badge_text_pos, text_color,
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, badge_text_pos, text_color,
IMSTR_START_END(description), badge_text_width, &clip_rect);
badge_text_pos.y += description_size.y;
}
@ -2670,16 +2546,17 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
// title
{
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, text_color,
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, text_pos, text_color,
TRANSLATE_DISAMBIG("Achievements", "Achievements Unlocked", "Pause Menu"));
const float unlocked_fraction = static_cast<float>(s_state.game_summary.num_unlocked_achievements) /
static_cast<float>(s_state.game_summary.num_core_achievements);
buffer.format("{}%", static_cast<u32>(std::round(unlocked_fraction * 100.0f)));
text_size = UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(buffer));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize,
text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.BoldFontWeight, FLT_MAX, 0.0f,
IMSTR_START_END(buffer));
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight,
ImVec2(text_pos.x + (box_content_width - text_size.x), text_pos.y), text_color,
IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFont->FontSize + paragraph_spacing;
text_pos.y += UIStyle.MediumFontSize + paragraph_spacing;
const ImRect progress_bb(text_pos, text_pos + ImVec2(box_content_width, progress_height));
const u32 progress_color = ImGui::GetColorU32(DarkerColor(UIStyle.SecondaryColor));
@ -2693,8 +2570,9 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
}
buffer.format("{}/{}", s_state.game_summary.num_unlocked_achievements, s_state.game_summary.num_core_achievements);
text_size = UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(buffer));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize,
text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.BoldFontWeight, FLT_MAX, 0.0f,
IMSTR_START_END(buffer));
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight,
ImVec2(progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f),
progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f)),
ImGui::GetColorU32(UIStyle.PrimaryTextColor), IMSTR_START_END(buffer));
@ -2705,17 +2583,18 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
buffer.format(ICON_EMOJI_WARNING " {}",
TRANSLATE_PLURAL_SSTR("Achievements", "%n unlocks have not been confirmed by the server.",
"Pause Menu", pending_count));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, title_text_color,
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, text_pos, title_text_color,
IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFont->FontSize + paragraph_spacing;
text_pos.y += UIStyle.MediumFontSize + paragraph_spacing;
}
}
if (s_state.most_recent_unlock.has_value())
{
buffer.format(ICON_FA_LOCK_OPEN " {}", TRANSLATE_DISAMBIG_SV("Achievements", "Most Recent", "Pause Menu"));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, text_color, IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFont->FontSize + paragraph_spacing;
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, text_pos, text_color,
IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFontSize + paragraph_spacing;
draw_achievement_with_summary(s_state.most_recent_unlock->title, s_state.most_recent_unlock->description,
s_state.most_recent_unlock->badge_path, false);
@ -2727,8 +2606,9 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
if (s_state.achievement_nearest_completion.has_value())
{
buffer.format(ICON_FA_LOCK " {}", TRANSLATE_DISAMBIG_SV("Achievements", "Nearest Completion", "Pause Menu"));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, text_color, IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFont->FontSize + paragraph_spacing;
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, text_pos, text_color,
IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFontSize + paragraph_spacing;
draw_achievement_with_summary(s_state.achievement_nearest_completion->title,
s_state.achievement_nearest_completion->description,
@ -2740,7 +2620,7 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
if (!s_state.active_challenge_indicators.empty())
{
box_height = box_padding + box_padding + UIStyle.MediumFont->FontSize;
box_height = box_padding + box_padding + UIStyle.MediumFontSize;
for (size_t i = 0; i < s_state.active_challenge_indicators.size(); i++)
{
const AchievementChallengeIndicator& indicator = s_state.active_challenge_indicators[i];
@ -2756,8 +2636,9 @@ void Achievements::DrawPauseMenuOverlays(float start_pos_y)
buffer.format(ICON_FA_STOPWATCH " {}",
TRANSLATE_DISAMBIG_SV("Achievements", "Active Challenge Achievements", "Pause Menu"));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, text_color, IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFont->FontSize;
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, text_pos, text_color,
IMSTR_START_END(buffer));
text_pos.y += UIStyle.MediumFontSize;
for (const AchievementChallengeIndicator& indicator : s_state.active_challenge_indicators)
{
@ -2848,22 +2729,22 @@ void Achievements::DrawAchievementsWindow()
SmallString text;
ImVec2 text_size;
close_window = (ImGuiFullscreen::FloatingButton(ICON_FA_WINDOW_CLOSE, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f,
true, UIStyle.LargeFont) ||
ImGuiFullscreen::WantsToCloseMenu());
close_window =
(ImGuiFullscreen::FloatingButton(ICON_FA_WINDOW_CLOSE, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true) ||
ImGuiFullscreen::WantsToCloseMenu());
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize));
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFontSize));
text.assign(s_state.game_title);
if (rc_client_get_hardcore_enabled(s_state.client))
text.append(TRANSLATE_SV("Achievements", " (Hardcore Mode)"));
top += UIStyle.LargeFont->FontSize + spacing;
top += UIStyle.LargeFontSize + spacing;
RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, ImGui::GetColorU32(ImGuiCol_Text), text,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(ImGuiCol_Text), text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize));
const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFontSize));
if (s_state.game_summary.num_core_achievements > 0)
{
if (s_state.game_summary.num_unlocked_achievements == s_state.game_summary.num_core_achievements)
@ -2884,10 +2765,10 @@ void Achievements::DrawAchievementsWindow()
text.assign(TRANSLATE_SV("Achievements", "This game has no achievements."));
}
top += UIStyle.MediumFont->FontSize + spacing;
top += UIStyle.MediumFontSize + spacing;
RenderShadowedTextClipped(
UIStyle.MediumFont, summary_bb.Min, summary_bb.Max,
UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, summary_bb.Min, summary_bb.Max,
ImGui::GetColorU32(ImGuiFullscreen::DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text])), text, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
@ -2908,12 +2789,12 @@ void Achievements::DrawAchievementsWindow()
}
text.format("{}%", static_cast<u32>(std::round(fraction * 100.0f)));
text_size =
UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(text));
text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.BoldFontWeight, FLT_MAX, 0.0f,
IMSTR_START_END(text));
const ImVec2 text_pos(
progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f),
progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos,
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, text_pos,
ImGui::GetColorU32(UIStyle.PrimaryTextColor), IMSTR_START_END(text));
// top += progress_height + spacing;
}
@ -3024,15 +2905,15 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
const std::string_view measured_progress(cheevo->measured_progress);
const bool is_measured = !is_unlocked && !measured_progress.empty();
const float unlock_rarity_height = spacing_unscaled + ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE;
const ImVec2 points_template_size = UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f,
TRANSLATE("Achievements", "XXX points"));
const ImVec2 points_template_size = UIStyle.Font->CalcTextSizeA(
UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX, 0.0f, TRANSLATE("Achievements", "XXX points"));
const size_t summary_length = std::strlen(cheevo->description);
const float summary_wrap_width =
(ImGui::GetCurrentWindow()->WorkRect.GetWidth() - (ImGui::GetStyle().FramePadding.x * 2.0f) -
LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT + 30.0f) - points_template_size.x);
const ImVec2 summary_text_size(UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX,
summary_wrap_width, cheevo->description,
cheevo->description + summary_length));
const ImVec2 summary_text_size =
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX, summary_wrap_width,
cheevo->description, cheevo->description + summary_length);
// Messy, but need to undo LayoutScale in MenuButtonFrame()...
const float extra_summary_height = std::max(LayoutUnscale(summary_text_size.y) - LAYOUT_MENU_BUTTON_HEIGHT, 0.0f);
@ -3066,10 +2947,10 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
SmallString text;
const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + spacing;
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + spacing;
text = TRANSLATE_PLURAL_SSTR("Achievements", "%n points", "Achievement points", cheevo->points);
const ImVec2 points_size(
UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(text)));
const ImVec2 points_size =
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX, 0.0f, IMSTR_START_END(text));
const float points_template_start = bb.Max.x - points_template_size.x;
const float points_start = points_template_start + ((points_template_size.x - points_size.x) * 0.5f);
@ -3095,8 +2976,8 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
break;
}
const ImVec2 right_icon_size =
UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(right_icon_text));
const ImVec2 right_icon_size = UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight, FLT_MAX,
0.0f, IMSTR_START_END(right_icon_text));
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(points_start, midpoint));
@ -3108,18 +2989,18 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
const ImRect lock_bb(ImVec2(points_template_start + ((points_template_size.x - right_icon_size.x) * 0.5f), bb.Min.y),
ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, cheevo->title, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.LargeFont, lock_bb.Min, lock_bb.Max, text_color, right_icon_text, &right_icon_size,
ImVec2(0.0f, 0.0f), 0.0f, &lock_bb);
RenderShadowedTextClipped(UIStyle.MediumFont, points_bb.Min, points_bb.Max, summary_color, text, &points_size,
ImVec2(0.0f, 0.0f), 0.0f, &points_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
text_color, cheevo->title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, lock_bb.Min, lock_bb.Max,
text_color, right_icon_text, &right_icon_size, ImVec2(0.0f, 0.0f), 0.0f, &lock_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, points_bb.Min,
points_bb.Max, summary_color, text, &points_size, ImVec2(0.0f, 0.0f), 0.0f, &points_bb);
if (cheevo->description && summary_length > 0)
{
RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, summary_color,
std::string_view(cheevo->description, summary_length), &summary_text_size,
ImVec2(0.0f, 0.0f), summary_wrap_width, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, summary_color, std::string_view(cheevo->description, summary_length),
&summary_text_size, ImVec2(0.0f, 0.0f), summary_wrap_width, &summary_bb);
}
// display hc if hc is active
@ -3132,14 +3013,16 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
text.format(TRANSLATE_FS("Achievements", "Unlocked: {} | {:.1f}% of players have this achievement"), date,
rarity_to_display);
RenderShadowedTextClipped(UIStyle.MediumFont, unlock_rarity_bb.Min, unlock_rarity_bb.Max, rarity_color, text,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &unlock_rarity_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, unlock_rarity_bb.Min,
unlock_rarity_bb.Max, rarity_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f,
&unlock_rarity_bb);
}
else
{
text.format(TRANSLATE_FS("Achievements", "{:.1f}% of players have this achievement"), rarity_to_display);
RenderShadowedTextClipped(UIStyle.MediumFont, unlock_rarity_bb.Min, unlock_rarity_bb.Max, rarity_color, text,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &unlock_rarity_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, unlock_rarity_bb.Min,
unlock_rarity_bb.Max, rarity_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f,
&unlock_rarity_bb);
}
if (!is_unlocked && is_measured)
@ -3157,11 +3040,11 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
dl->AddRectFilled(progress_bb.Min, ImVec2(progress_bb.Min.x + fraction * progress_bb.GetWidth(), progress_bb.Max.y),
ImGui::GetColorU32(ImGuiFullscreen::UIStyle.SecondaryColor), progress_rounding);
const ImVec2 text_size = UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f,
IMSTR_START_END(measured_progress));
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
0.0f, IMSTR_START_END(measured_progress));
const ImVec2 text_pos(progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f),
progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos,
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, text_pos,
ImGui::GetColorU32(ImGuiFullscreen::UIStyle.PrimaryTextColor), IMSTR_START_END(measured_progress));
}
@ -3235,15 +3118,17 @@ void Achievements::DrawLeaderboardsWindow()
}
const float rank_column_width =
UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits<float>::max(), -1.0f, "99999").x;
const float name_column_width =
UIStyle.LargeFont
->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits<float>::max(), -1.0f, "WWWWWWWWWWWWWWWWWWWWWW")
.x;
const float time_column_width =
UIStyle.LargeFont
->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits<float>::max(), -1.0f, "WWWWWWWWWWW")
UIStyle.Font
->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight, std::numeric_limits<float>::max(), -1.0f, "99999")
.x;
const float name_column_width = UIStyle.Font
->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight,
std::numeric_limits<float>::max(), -1.0f, "WWWWWWWWWWWWWWWWWWWWWW")
.x;
const float time_column_width = UIStyle.Font
->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight,
std::numeric_limits<float>::max(), -1.0f, "WWWWWWWWWWW")
.x;
const float column_spacing = spacing * 2.0f;
if (ImGuiFullscreen::BeginFullscreenWindow(
@ -3278,8 +3163,7 @@ void Achievements::DrawLeaderboardsWindow()
if (!is_leaderboard_open)
{
if (ImGuiFullscreen::FloatingButton(ICON_FA_WINDOW_CLOSE, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true,
UIStyle.LargeFont) ||
if (ImGuiFullscreen::FloatingButton(ICON_FA_WINDOW_CLOSE, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true) ||
ImGuiFullscreen::WantsToCloseMenu())
{
FullscreenUI::ReturnToPreviousWindow();
@ -3287,31 +3171,31 @@ void Achievements::DrawLeaderboardsWindow()
}
else
{
if (ImGuiFullscreen::FloatingButton(ICON_FA_CARET_SQUARE_LEFT, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true,
UIStyle.LargeFont) ||
if (ImGuiFullscreen::FloatingButton(ICON_FA_CARET_SQUARE_LEFT, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true) ||
ImGuiFullscreen::WantsToCloseMenu())
{
close_leaderboard_on_exit = true;
}
}
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize));
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFontSize));
text.assign(Achievements::GetGameTitle());
top += UIStyle.LargeFont->FontSize + spacing;
top += UIStyle.LargeFontSize + spacing_small;
RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, text, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
text_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
u32 summary_color;
if (is_leaderboard_open)
{
const ImRect subtitle_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize));
const ImRect subtitle_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFontSize));
text.assign(s_state.open_leaderboard->title);
top += UIStyle.LargeFont->FontSize + spacing_small;
top += UIStyle.LargeFontSize + spacing_small;
RenderShadowedTextClipped(UIStyle.LargeFont, subtitle_bb.Min, subtitle_bb.Max,
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, subtitle_bb.Min,
subtitle_bb.Max,
ImGui::GetColorU32(DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text])), text,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &subtitle_bb);
@ -3327,23 +3211,24 @@ void Achievements::DrawLeaderboardsWindow()
summary_color = ImGui::GetColorU32(DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text]));
}
const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize));
top += UIStyle.MediumFont->FontSize + spacing_small;
const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFontSize));
top += UIStyle.MediumFontSize + spacing_small;
RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max, summary_color, text, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, summary_bb.Min,
summary_bb.Max, summary_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
if (!is_leaderboard_open && !Achievements::IsHardcoreModeActive())
{
const ImRect hardcore_warning_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize));
top += UIStyle.MediumFont->FontSize + spacing_small;
const ImRect hardcore_warning_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFontSize));
top += UIStyle.MediumFontSize + spacing_small;
text.format(
ICON_EMOJI_WARNING " {}",
TRANSLATE_SV("Achievements",
"Submitting scores is disabled because hardcore mode is off. Leaderboards are read-only."));
RenderShadowedTextClipped(UIStyle.MediumFont, hardcore_warning_bb.Min, hardcore_warning_bb.Max,
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, hardcore_warning_bb.Min,
hardcore_warning_bb.Max,
ImGui::GetColorU32(DarkerColor(DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text]))),
text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &hardcore_warning_bb);
}
@ -3394,17 +3279,19 @@ void Achievements::DrawLeaderboardsWindow()
const u32 heading_color = ImGui::GetColorU32(DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text]));
const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
float text_start_x = bb.Min.x + LayoutScale(15.0f) + padding;
const ImRect rank_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, rank_bb.Min, rank_bb.Max, heading_color,
TRANSLATE_SV("Achievements", "Rank"), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &rank_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, rank_bb.Min, rank_bb.Max,
heading_color, TRANSLATE_SV("Achievements", "Rank"), nullptr, ImVec2(0.0f, 0.0f),
0.0f, &rank_bb);
text_start_x += rank_column_width + column_spacing;
const ImRect user_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, user_bb.Min, user_bb.Max, heading_color,
TRANSLATE_SV("Achievements", "Name"), nullptr, ImVec2(0.0f, 0.0f), 0.0f, &user_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, user_bb.Min, user_bb.Max,
heading_color, TRANSLATE_SV("Achievements", "Name"), nullptr, ImVec2(0.0f, 0.0f),
0.0f, &user_bb);
text_start_x += name_column_width + column_spacing;
static const char* value_headings[NUM_RC_CLIENT_LEADERBOARD_FORMATS] = {
@ -3415,7 +3302,7 @@ void Achievements::DrawLeaderboardsWindow()
const ImRect score_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(
UIStyle.LargeFont, score_bb.Min, score_bb.Max, heading_color,
UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, score_bb.Min, score_bb.Max, heading_color,
Host::TranslateToStringView(
"Achievements",
value_headings[std::min<u8>(s_state.open_leaderboard->format, NUM_RC_CLIENT_LEADERBOARD_FORMATS - 1)]),
@ -3423,13 +3310,13 @@ void Achievements::DrawLeaderboardsWindow()
text_start_x += time_column_width + column_spacing;
const ImRect date_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, date_bb.Min, date_bb.Max, heading_color,
TRANSLATE_SV("Achievements", "Date Submitted"), nullptr, ImVec2(0.0f, 0.0f), 0.0f,
&date_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, date_bb.Min, date_bb.Max,
heading_color, TRANSLATE_SV("Achievements", "Date Submitted"), nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &date_bb);
const float line_thickness = LayoutScale(1.0f);
const float line_padding = LayoutScale(5.0f);
const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding);
const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFontSize + line_padding);
const ImVec2 line_end(bb.Max.x, line_start.y);
ImGui::GetWindowDrawList()->AddLine(line_start, line_end, ImGui::GetColorU32(ImGuiCol_TextDisabled),
line_thickness);
@ -3500,7 +3387,8 @@ void Achievements::DrawLeaderboardsWindow()
{
const ImVec2 pos_min(0.0f, heading_height);
const ImVec2 pos_max(display_size.x, display_size.y);
RenderShadowedTextClipped(UIStyle.LargeFont, pos_min, pos_max, text_color,
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, pos_min, pos_max,
text_color,
TRANSLATE_SV("Achievements", "Downloading leaderboard data, please wait..."),
nullptr, ImVec2(0.5f, 0.5f), 0.0f);
}
@ -3523,11 +3411,11 @@ void Achievements::DrawLeaderboardsWindow()
&hovered, &bb.Min, &bb.Max);
if (visible)
{
const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, text_color, text, nullptr,
ImVec2(0, 0), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min,
title_bb.Max, text_color, text, nullptr, ImVec2(0, 0), 0.0f, &title_bb);
if (!s_state.leaderboard_fetch_handle)
FetchNextLeaderboardEntries();
@ -3574,7 +3462,7 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent
if (!visible)
return;
const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
float text_start_x = bb.Min.x + LayoutScale(15.0f);
SmallString text;
@ -3587,8 +3475,8 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent
ImGui::GetStyle().Colors[ImGuiCol_Text]);
const ImRect rank_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, rank_bb.Min, rank_bb.Max, text_color, text, nullptr, ImVec2(0.0f, 0.0f),
0.0f, &rank_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, rank_bb.Min, rank_bb.Max,
text_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &rank_bb);
text_start_x += rank_column_width + column_spacing;
const float icon_size = bb.Max.y - bb.Min.y;
@ -3618,20 +3506,20 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent
}
const ImRect user_bb(ImVec2(text_start_x + column_spacing + icon_size, bb.Min.y), ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, user_bb.Min, user_bb.Max, text_color, entry.user, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &user_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, user_bb.Min, user_bb.Max,
text_color, entry.user, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &user_bb);
text_start_x += name_column_width + column_spacing;
const ImRect score_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
RenderShadowedTextClipped(UIStyle.LargeFont, score_bb.Min, score_bb.Max, text_color, entry.display, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &score_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, score_bb.Min, score_bb.Max,
text_color, entry.display, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &score_bb);
text_start_x += time_column_width + column_spacing;
const ImRect time_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
SmallString submit_time;
FullscreenUI::TimeToPrintableString(&submit_time, entry.submitted);
RenderShadowedTextClipped(UIStyle.LargeFont, time_bb.Min, time_bb.Max, text_color, submit_time, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &time_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, time_bb.Min, time_bb.Max,
text_color, submit_time, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &time_bb);
if (pressed)
{
@ -3658,17 +3546,19 @@ void Achievements::DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboar
if (!visible)
return;
const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const float text_start_x = bb.Min.x + LayoutScale(15.0f);
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max);
RenderShadowedTextClipped(UIStyle.LargeFont, title_bb.Min, title_bb.Max, ImGui::GetColorU32(ImGuiCol_Text),
lboard->title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(ImGuiCol_Text), lboard->title, nullptr, ImVec2(0.0f, 0.0f), 0.0f,
&title_bb);
if (lboard->description && lboard->description[0] != '\0')
{
RenderShadowedTextClipped(UIStyle.MediumFont, summary_bb.Min, summary_bb.Max,
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max,
ImGui::GetColorU32(ImGuiFullscreen::DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text])),
lboard->description, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
}

File diff suppressed because it is too large Load Diff

View File

@ -585,7 +585,7 @@ void GPUBackend::GetStatsString(SmallStringBase& str) const
{
if (g_gpu_settings.gpu_pgxp_depth_buffer)
{
str.format("{}{} HW | {} P | {} DC | {} B | {} RP | {} RB | {} C | {} W | {} DBC",
str.format("\x02{}{} HW | \x01{}\x02 P | \x01{}\x02 DC | \x01{}\x02 B | \x01{}\x02 RP | \x01{}\x02 RB | \x01{}\x02 C | \x01{}\x02 W | \x01{}\x02 DBC",
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()), g_gpu_settings.gpu_use_thread ? "-MT" : "",
s_stats.num_primitives, s_stats.host_num_draws, s_stats.host_num_barriers,
s_stats.host_num_render_passes, s_stats.host_num_downloads, s_stats.num_copies, s_stats.num_writes,
@ -593,7 +593,7 @@ void GPUBackend::GetStatsString(SmallStringBase& str) const
}
else
{
str.format("{}{} HW | {} P | {} DC | {} B | {} RP | {} RB | {} C | {} W",
str.format("\x02{}{} HW | \x01{}\x02 P | \x01{}\x02 DC | \x01{}\x02 B | \x01{}\x02 RP | \x01{}\x02 RB | \x01{}\x02 C | \x01{}\x02 W",
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()), g_gpu_settings.gpu_use_thread ? "-MT" : "",
s_stats.num_primitives, s_stats.host_num_draws, s_stats.host_num_barriers,
s_stats.host_num_render_passes, s_stats.host_num_downloads, s_stats.num_copies, s_stats.num_writes);

View File

@ -54,6 +54,8 @@ LOG_CHANNEL(ImGuiManager);
namespace ImGuiManager {
static constexpr float FIXED_BOLD_WEIGHT = 700.0f;
namespace {
struct InputOverlayState
@ -312,6 +314,106 @@ void ImGuiManager::SetStatusIndicatorIcons(SmallStringBase& text, bool paused)
text.pop_back();
}
static void DrawPerformanceStat(ImDrawList* dl, float& position_y, ImFont* font, float size, float alt_weight,
ImU32 alt_color, float shadow_offset, float rbounds, std::string_view text)
{
static constexpr auto find_control_char = [](const std::string_view& sv, std::string_view::size_type pos) {
const size_t len = sv.length();
for (; pos < len; pos++)
{
if (sv[pos] <= '\x04')
break;
}
return pos;
};
constexpr ImU32 color = IM_COL32(255, 255, 255, 255);
constexpr float default_weight = 0.0f;
std::string_view::size_type pos = 0;
float current_weight = default_weight;
float width = 0.0f;
float height = 0.0f;
do
{
if (text[pos] >= '\x01' && text[pos] <= '\x04')
{
current_weight = (text[pos] == '\x02') ? alt_weight : ((text[pos] == '\x01') ? default_weight : current_weight);
pos++;
}
std::string_view::size_type epos = find_control_char(text, pos);
const char* start_ptr = text.data() + pos;
const char* end_ptr = text.data() + epos;
if (start_ptr != end_ptr)
{
const ImVec2 text_size = font->CalcTextSizeA(size, current_weight, FLT_MAX, 0.0f, start_ptr, end_ptr);
width += text_size.x;
height = std::max(height, text_size.y);
}
pos = epos;
} while (pos < text.length());
ImVec2 position = ImVec2(rbounds - width, position_y);
ImU32 current_color = color;
pos = 0;
current_weight = default_weight;
do
{
const char ch = text[pos];
if (ch >= '\x01' && ch <= '\x02')
{
current_weight = (ch == '\x02') ? alt_weight : default_weight;
pos++;
}
else if (ch >= '\x03' && ch <= '\x04')
{
current_color = (ch == '\x04') ? alt_color : color;
pos++;
}
std::string_view::size_type epos = find_control_char(text, pos);
const char* start_ptr = text.data() + pos;
const char* end_ptr = text.data() + ((epos == std::string_view::npos) ? text.length() : epos);
if (start_ptr != end_ptr)
{
dl->AddText(font, size, current_weight, ImVec2(position.x + shadow_offset, position.y + shadow_offset),
IM_COL32(0, 0, 0, 100), start_ptr, end_ptr);
dl->AddText(font, size, current_weight, position, current_color, start_ptr, end_ptr);
position.x += font->CalcTextSizeA(size, current_weight, FLT_MAX, 0.0f, start_ptr, end_ptr).x;
}
pos = epos;
} while (pos < text.length());
position_y += height;
}
#if 0
static void DrawMultiLinePerformanceStat(ImDrawList* dl, float& position_y, ImFont* font, float size, float alt_weight,
ImU32 alt_color, float shadow_offset, float rbounds, float spacing,
std::string_view text)
{
std::string_view::size_type pos = 0;
for (;;)
{
const std::string_view::size_type next_line_pos = text.find('\n', pos);
const std::string_view line =
text.substr(pos, (next_line_pos == std::string_view::npos) ? std::string_view::npos : (next_line_pos - pos));
if (!line.empty())
{
DrawPerformanceStat(dl, position_y, font, size, alt_weight, 0, shadow_offset, rbounds, line);
position_y += spacing;
}
if (next_line_pos == std::string_view::npos)
break;
pos = next_line_pos + 1;
}
}
#endif
void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position_y, float scale, float margin,
float spacing)
{
@ -324,8 +426,11 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
}
const float shadow_offset = std::ceil(1.0f * scale);
ImFont* fixed_font = ImGuiManager::GetFixedFont();
ImFont* standard_font = ImGuiManager::GetOSDFont();
ImFont* const fixed_font = ImGuiManager::GetFixedFont();
const float fixed_font_size = ImGuiManager::GetFixedFontSize();
ImFont* ui_font = ImGuiManager::GetTextFont();
const float ui_font_size = ImGuiManager::GetOSDFontSize();
const float rbound = ImGui::GetIO().DisplaySize.x - margin;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
SmallString text;
ImVec2 text_size;
@ -334,13 +439,13 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
do \
{ \
text_size = \
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
font->CalcTextSizeA(font##_size, 0.0f, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText( \
font, font->FontSize, \
font, font##_size, 0.0f, \
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), text.c_str(), text.end_ptr()); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, \
(text)); \
dl->AddText(font, font##_size, 0.0f, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), \
color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
@ -348,37 +453,44 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
{
const float speed = PerformanceCounters::GetEmulationSpeed();
if (g_gpu_settings.display_show_fps)
text.append_format("G: {:.2f} | V: {:.2f}", PerformanceCounters::GetFPS(), PerformanceCounters::GetVPS());
text.append_format("\x02G: \x04{:.2f}\x03\x02 | V: \x01\x04{:.2f}\x03", PerformanceCounters::GetFPS(),
PerformanceCounters::GetVPS());
if (g_gpu_settings.display_show_speed)
{
text.append_format("{}{}%", text.empty() ? "" : " | ", static_cast<u32>(std::round(speed)));
text.append_format("\x02{}\x04{}% \x01\x03", text.empty() ? "" : " | ", static_cast<u32>(std::round(speed)));
const float target_speed = System::GetTargetSpeed();
if (target_speed <= 0.0f)
text.append(" (Max)");
text.append("(Max)");
else
text.append_format(" ({:.0f}%)", target_speed * 100.0f);
text.append_format("({:.0f}%)", target_speed * 100.0f);
}
if (!text.empty())
{
ImU32 color;
ImU32 alt_color;
if (speed < 95.0f)
color = IM_COL32(255, 100, 100, 255);
alt_color = IM_COL32(255, 100, 100, 255);
else if (speed > 105.0f)
color = IM_COL32(100, 255, 100, 255);
alt_color = IM_COL32(100, 255, 100, 255);
else
color = IM_COL32(255, 255, 255, 255);
alt_color = IM_COL32(255, 255, 255, 255);
DRAW_LINE(fixed_font, text, color);
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, alt_color, shadow_offset,
rbound, text);
position_y += spacing;
}
if (g_gpu_settings.display_show_gpu_stats)
{
gpu->GetStatsString(text);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
gpu->GetMemoryStatsString(text);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
}
if (g_gpu_settings.display_show_resolution)
@ -389,27 +501,34 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
const bool pal = g_gpu.IsInPALMode();
text.format("{}x{} {} {} [{}x]", display_width * resolution_scale, display_height * resolution_scale,
pal ? "PAL" : "NTSC", interlaced ? "Interlaced" : "Progressive", resolution_scale);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
}
if (g_gpu_settings.display_show_latency_stats)
{
System::FormatLatencyStats(text);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
}
if (g_gpu_settings.display_show_cpu_usage)
{
text.format("{:.2f}ms | {:.2f}ms | {:.2f}ms", PerformanceCounters::GetMinimumFrameTime(),
PerformanceCounters::GetAverageFrameTime(), PerformanceCounters::GetMaximumFrameTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
if (g_settings.cpu_overclock_active || CPU::g_state.using_interpreter ||
g_settings.cpu_execution_mode != CPUExecutionMode::Recompiler || g_settings.cpu_recompiler_icache ||
g_settings.cpu_recompiler_memory_exceptions)
{
bool first = true;
text.assign("CPU[");
text.assign("\x02"
"CPU[");
if (g_settings.cpu_overclock_active)
{
text.append_format("{}", g_settings.GetCPUOverclockPercent());
@ -434,52 +553,62 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
text.append_format("{}{}", first ? "" : "/", "ME");
}
text.append("]: ");
text.append("]: \x01");
}
else
{
text.assign("CPU: ");
text.assign("\x02"
"CPU: \x01");
}
FormatProcessorStat(text, PerformanceCounters::GetCPUThreadUsage(),
PerformanceCounters::GetCPUThreadAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
if (g_gpu_settings.gpu_use_thread)
{
text.assign("RNDR: ");
text.assign("\x02RNDR: \x01");
FormatProcessorStat(text, PerformanceCounters::GetGPUThreadUsage(),
PerformanceCounters::GetGPUThreadAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
}
#ifndef __ANDROID__
if (MediaCapture* cap = System::GetMediaCapture())
{
text.assign("CAP: ");
text.assign("\x02"
"CAP: \x01");
FormatProcessorStat(text, cap->GetCaptureThreadUsage(), cap->GetCaptureThreadTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
}
#endif
}
if (g_gpu_settings.display_show_gpu_usage && g_gpu_device->IsGPUTimingEnabled())
{
text.assign("GPU: ");
text.assign("\x02GPU: \x01");
FormatProcessorStat(text, PerformanceCounters::GetGPUUsage(), PerformanceCounters::GetGPUAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
DrawPerformanceStat(dl, position_y, fixed_font, fixed_font_size, FIXED_BOLD_WEIGHT, 0, shadow_offset, rbound,
text);
position_y += spacing;
}
if (g_gpu_settings.display_show_status_indicators)
{
SetStatusIndicatorIcons(text, false);
if (!text.empty())
DRAW_LINE(standard_font, text, IM_COL32(255, 255, 255, 255));
DRAW_LINE(ui_font, text, IM_COL32(255, 255, 255, 255));
}
}
else if (g_gpu_settings.display_show_status_indicators && !FullscreenUI::HasActiveWindow())
{
SetStatusIndicatorIcons(text, true);
DRAW_LINE(standard_font, text, IM_COL32(255, 255, 255, 255));
DRAW_LINE(ui_font, text, IM_COL32(255, 255, 255, 255));
}
#undef DRAW_LINE
@ -558,16 +687,18 @@ void ImGuiManager::DrawEnhancementsOverlay(const GPUBackend* gpu)
const float scale = ImGuiManager::GetGlobalScale();
const float shadow_offset = 1.0f * scale;
const float margin = ImGuiManager::GetScreenMargin() * scale;
ImFont* font = ImGuiManager::GetFixedFont();
const float position_y = ImGui::GetIO().DisplaySize.y - margin - font->FontSize;
ImFont* const font = ImGuiManager::GetFixedFont();
const float font_size = ImGuiManager::GetFixedFontSize();
const float font_weight = 0.0f;
const float position_y = ImGui::GetIO().DisplaySize.y - margin - font_size;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
ImVec2 text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, text.c_str(),
ImVec2 text_size = font->CalcTextSizeA(font_size, font_weight, std::numeric_limits<float>::max(), -1.0f, text.c_str(),
text.end_ptr(), nullptr);
dl->AddText(font, font->FontSize,
dl->AddText(font, font_size, font_weight,
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset),
IM_COL32(0, 0, 0, 100), text.c_str(), text.end_ptr());
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y),
dl->AddText(font, font_size, font_weight, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y),
IM_COL32(255, 255, 255, 255), text.c_str(), text.end_ptr());
}
@ -579,17 +710,19 @@ void ImGuiManager::DrawMediaCaptureOverlay(float& position_y, float scale, float
return;
const float shadow_offset = std::ceil(scale);
ImFont* const standard_font = ImGuiManager::GetOSDFont();
ImFont* const ui_font = ImGuiManager::GetTextFont();
const float ui_font_size = ImGuiManager::GetOSDFontSize();
const float ui_font_weight = 0.0f;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
static constexpr const char* ICON = ICON_PF_CIRCLE;
const time_t elapsed_time = cap->GetElapsedTime();
const TinyString text_msg = TinyString::from_format(" {:02d}:{:02d}:{:02d}", elapsed_time / 3600,
(elapsed_time % 3600) / 60, (elapsed_time % 3600) % 60);
const ImVec2 icon_size = standard_font->CalcTextSizeA(standard_font->FontSize, std::numeric_limits<float>::max(),
-1.0f, ICON, nullptr, nullptr);
const ImVec2 text_size = standard_font->CalcTextSizeA(standard_font->FontSize, std::numeric_limits<float>::max(),
-1.0f, text_msg.c_str(), text_msg.end_ptr(), nullptr);
const ImVec2 icon_size = ui_font->CalcTextSizeA(ui_font_size, ui_font_weight, std::numeric_limits<float>::max(),
-1.0f, ICON, nullptr, nullptr);
const ImVec2 text_size = ui_font->CalcTextSizeA(ui_font_size, ui_font_weight, std::numeric_limits<float>::max(),
-1.0f, text_msg.c_str(), text_msg.end_ptr(), nullptr);
const float box_margin = 5.0f * scale;
const ImVec2 box_size = ImVec2(ImCeil(icon_size.x + shadow_offset + text_size.x + box_margin * 2.0f),
@ -598,13 +731,13 @@ void ImGuiManager::DrawMediaCaptureOverlay(float& position_y, float scale, float
dl->AddRectFilled(box_pos, box_pos + box_size, IM_COL32(0, 0, 0, 64), box_margin);
const ImVec2 text_start = ImVec2(box_pos.x + box_margin, box_pos.y + box_margin);
dl->AddText(standard_font, standard_font->FontSize,
ImVec2(text_start.x + shadow_offset, text_start.y + shadow_offset), IM_COL32(0, 0, 0, 100), ICON);
dl->AddText(standard_font, standard_font->FontSize,
dl->AddText(ui_font, ui_font_size, ui_font_weight, ImVec2(text_start.x + shadow_offset, text_start.y + shadow_offset),
IM_COL32(0, 0, 0, 100), ICON);
dl->AddText(ui_font, ui_font_size, ui_font_weight,
ImVec2(text_start.x + icon_size.x + shadow_offset, text_start.y + shadow_offset), IM_COL32(0, 0, 0, 100),
text_msg.c_str(), text_msg.end_ptr());
dl->AddText(standard_font, standard_font->FontSize, text_start, IM_COL32(255, 0, 0, 255), ICON);
dl->AddText(standard_font, standard_font->FontSize, ImVec2(text_start.x + icon_size.x, text_start.y),
dl->AddText(ui_font, ui_font_size, ui_font_weight, text_start, IM_COL32(255, 0, 0, 255), ICON);
dl->AddText(ui_font, ui_font_size, ui_font_weight, ImVec2(text_start.x + icon_size.x, text_start.y),
IM_COL32(255, 255, 255, 255), text_msg.c_str(), text_msg.end_ptr());
position_y += box_size.y + spacing;
@ -618,6 +751,8 @@ void ImGuiManager::DrawFrameTimeOverlay(float& position_y, float scale, float ma
const float shadow_offset = std::ceil(1.0f * scale);
ImFont* fixed_font = ImGuiManager::GetFixedFont();
const float fixed_font_size = ImGuiManager::GetFixedFontSize();
const float fixed_font_weight = 0.0f;
const ImVec2 history_size(ImCeil(200.0f * scale), ImCeil(50.0f * scale));
ImGui::SetNextWindowSize(ImVec2(history_size.x, history_size.y));
@ -633,7 +768,7 @@ void ImGuiManager::DrawFrameTimeOverlay(float& position_y, float scale, float ma
if (ImGui::Begin("##frame_times", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoFocusOnAppearing))
{
ImGui::PushFont(fixed_font);
ImGui::PushFont(fixed_font, fixed_font_size, fixed_font_weight);
auto [min, max] = GetMinMax(PerformanceCounters::GetFrameTimeHistory());
@ -659,20 +794,19 @@ void ImGuiManager::DrawFrameTimeOverlay(float& position_y, float scale, float ma
TinyString text;
text.format("{:.1f} ms", max);
ImVec2 text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.end_ptr());
ImVec2 text_size = fixed_font->CalcTextSizeA(fixed_font_size, -1.0f, FLT_MAX, 0.0f, text.c_str(), text.end_ptr());
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing + shadow_offset, wpos.y + shadow_offset),
IM_COL32(0, 0, 0, 100), text.c_str(), text.end_ptr());
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing, wpos.y), IM_COL32(255, 255, 255, 255),
text.c_str(), text.end_ptr());
text.format("{:.1f} ms", min);
text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.end_ptr());
text_size = fixed_font->CalcTextSizeA(fixed_font_size, -1.0f, FLT_MAX, 0.0f, text.c_str(), text.end_ptr());
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing + shadow_offset,
wpos.y + history_size.y - fixed_font->FontSize + shadow_offset),
wpos.y + history_size.y - fixed_font_size + shadow_offset),
IM_COL32(0, 0, 0, 100), text.c_str(), text.end_ptr());
win_dl->AddText(
ImVec2(wpos.x + history_size.x - text_size.x - spacing, wpos.y + history_size.y - fixed_font->FontSize),
IM_COL32(255, 255, 255, 255), text.c_str(), text.end_ptr());
win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing, wpos.y + history_size.y - fixed_font_size),
IM_COL32(255, 255, 255, 255), text.c_str(), text.end_ptr());
ImGui::PopFont();
}
ImGui::End();
@ -752,7 +886,9 @@ void ImGuiManager::DrawInputsOverlay()
const float shadow_offset = ImCeil(1.0f * scale);
const float margin = ImGuiManager::GetScreenMargin() * scale;
const float spacing = ImCeil(5.0f * scale);
ImFont* font = ImGuiManager::GetOSDFont();
ImFont* const font = ImGuiManager::GetTextFont();
const float font_size = ImGuiManager::GetOSDFontSize();
const float font_weight = 400.0f;
static constexpr u32 text_color = IM_COL32(0xff, 0xff, 0xff, 255);
static constexpr u32 shadow_color = IM_COL32(0x00, 0x00, 0x00, 100);
@ -763,7 +899,7 @@ void ImGuiManager::DrawInputsOverlay()
float current_x = ImFloor(margin);
float current_y =
ImFloor(display_size.y - margin -
((static_cast<float>(s_input_overlay_state.num_active_pads) * (font->FontSize + spacing)) - spacing));
((static_cast<float>(s_input_overlay_state.num_active_pads) * (font_size + spacing)) - spacing));
// This is a bit of a pain. Some of the glyphs slightly overhang/overshoot past the baseline, resulting
// in the glyphs getting clipped if we use the text height/margin as a clip point. Instead, just clamp it
@ -781,11 +917,11 @@ void ImGuiManager::DrawInputsOverlay()
float text_start_x = current_x;
if (cinfo.icon_name)
{
const ImVec2 icon_size = font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, cinfo.icon_name);
dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color,
cinfo.icon_name, nullptr, 0.0f, &clip_rect);
dl->AddText(font, font->FontSize, ImVec2(current_x, current_y), pstate.icon_color, cinfo.icon_name, nullptr, 0.0f,
&clip_rect);
const ImVec2 icon_size = font->CalcTextSizeA(font_size, font_weight, FLT_MAX, 0.0f, cinfo.icon_name);
dl->AddText(font, font_size, font_weight, ImVec2(current_x + shadow_offset, current_y + shadow_offset),
shadow_color, cinfo.icon_name, nullptr, 0.0f, &clip_rect);
dl->AddText(font, font_size, font_weight, ImVec2(current_x, current_y), pstate.icon_color, cinfo.icon_name,
nullptr, 0.0f, &clip_rect);
text_start_x += icon_size.x;
text.format(" {}", port_label);
}
@ -816,12 +952,12 @@ void ImGuiManager::DrawInputsOverlay()
}
}
dl->AddText(font, font->FontSize, ImVec2(text_start_x + shadow_offset, current_y + shadow_offset), shadow_color,
text.c_str(), text.end_ptr(), 0.0f, &clip_rect);
dl->AddText(font, font->FontSize, ImVec2(text_start_x, current_y), text_color, text.c_str(), text.end_ptr(), 0.0f,
&clip_rect);
dl->AddText(font, font_size, font_weight, ImVec2(text_start_x + shadow_offset, current_y + shadow_offset),
shadow_color, text.c_str(), text.end_ptr(), 0.0f, &clip_rect);
dl->AddText(font, font_size, font_weight, ImVec2(text_start_x, current_y), text_color, text.c_str(), text.end_ptr(),
0.0f, &clip_rect);
current_y += font->FontSize + spacing;
current_y += font_size + spacing;
}
}
@ -1119,7 +1255,7 @@ void SaveStateSelectorUI::Draw()
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, UIStyle.BackgroundColor);
ImGui::PushStyleColor(ImGuiCol_WindowBg, DarkerColor(UIStyle.PopupBackgroundColor));
ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor);
ImGui::PushFont(ImGuiManager::GetOSDFont());
ImGui::PushFont(ImGuiManager::GetTextFont(), ImGuiManager::GetOSDFontSize());
ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), ImGuiCond_Always,
ImVec2(0.5f, 0.5f));

View File

@ -57,8 +57,10 @@ namespace MiniHost {
/// Use two async worker threads, should be enough for most tasks.
static constexpr u32 NUM_ASYNC_WORKER_THREADS = 2;
static constexpr u32 DEFAULT_WINDOW_WIDTH = 1280;
static constexpr u32 DEFAULT_WINDOW_HEIGHT = 720;
//static constexpr u32 DEFAULT_WINDOW_WIDTH = 1280;
//static constexpr u32 DEFAULT_WINDOW_HEIGHT = 720;
static constexpr u32 DEFAULT_WINDOW_WIDTH = 1920;
static constexpr u32 DEFAULT_WINDOW_HEIGHT = 1080;
static constexpr u32 SETTINGS_VERSION = 3;
static constexpr auto CPU_THREAD_POLL_INTERVAL =
@ -304,9 +306,6 @@ bool MiniHost::InitializeConfig()
if (!Log::IsConsoleOutputEnabled() && s_state.base_settings_interface.GetBoolValue("Logging", "LogToConsole", false))
Log::SetConsoleOutputParams(true, s_state.base_settings_interface.GetBoolValue("Logging", "LogTimestamps", true));
// imgui setup, make sure it doesn't bug out
ImGuiManager::SetFontPathAndRange(std::string(), {0x0020, 0x00FF, 0x2022, 0x2022, 0, 0});
return true;
}

View File

@ -399,81 +399,6 @@ bool QtHost::DownloadFile(QWidget* parent, const QString& title, std::string url
return true;
}
bool QtHost::DownloadFileFromZip(QWidget* parent, const QString& title, std::string url, const char* zip_filename,
const char* output_path)
{
INFO_LOG("Download {} from {}, saving to {}.", zip_filename, url, output_path);
std::vector<u8> data;
if (!DownloadFile(parent, title, std::move(url), &data).value_or(false) || data.empty())
return false;
const unzFile zf = MinizipHelpers::OpenUnzMemoryFile(data.data(), data.size());
if (!zf)
{
QMessageBox::critical(parent, qApp->translate("QtHost", "Error"),
qApp->translate("QtHost", "Failed to open downloaded zip file."));
return false;
}
const ScopedGuard zf_guard = [&zf]() { unzClose(zf); };
if (unzLocateFile(zf, zip_filename, 0) != UNZ_OK || unzOpenCurrentFile(zf) != UNZ_OK)
{
QMessageBox::critical(
parent, qApp->translate("QtHost", "Error"),
qApp->translate("QtHost", "Failed to locate '%1' in zip.").arg(QString::fromUtf8(zip_filename)));
return false;
}
// Directory may not exist. Create it.
Error error;
FileSystem::ManagedCFilePtr output_file;
const std::string directory(Path::GetDirectory(output_path));
if ((!directory.empty() && !FileSystem::DirectoryExists(directory.c_str()) &&
!FileSystem::CreateDirectory(directory.c_str(), true)) ||
!(output_file = FileSystem::OpenManagedCFile(output_path, "wb", &error)))
{
QMessageBox::critical(parent, qApp->translate("QtHost", "Error"),
qApp->translate("QtHost", "Failed to open '%1': %2.")
.arg(QString::fromUtf8(output_path))
.arg(QString::fromStdString(error.GetDescription())));
return false;
}
static constexpr size_t CHUNK_SIZE = 4096;
char chunk[CHUNK_SIZE];
for (;;)
{
int size = unzReadCurrentFile(zf, chunk, CHUNK_SIZE);
if (size < 0)
{
QMessageBox::critical(
parent, qApp->translate("QtHost", "Error"),
qApp->translate("QtHost", "Failed to read '%1' from zip.").arg(QString::fromUtf8(zip_filename)));
output_file.reset();
FileSystem::DeleteFile(output_path);
return false;
}
else if (size == 0)
{
break;
}
if (std::fwrite(chunk, size, 1, output_file.get()) != 1)
{
QMessageBox::critical(parent, qApp->translate("QtHost", "Error"),
qApp->translate("QtHost", "Failed to write to '%1'.").arg(QString::fromUtf8(output_path)));
output_file.reset();
FileSystem::DeleteFile(output_path);
return false;
}
}
return true;
}
bool QtHost::InitializeConfig()
{
if (!SetCriticalFolders())
@ -2398,13 +2323,19 @@ bool Host::ResourceFileExists(std::string_view filename, bool allow_override)
std::optional<DynamicHeapArray<u8>> Host::ReadResourceFile(std::string_view filename, bool allow_override, Error* error)
{
const std::string path = QtHost::GetResourcePath(filename, allow_override);
return FileSystem::ReadBinaryFile(path.c_str(), error);
const std::optional<DynamicHeapArray<u8>> ret = FileSystem::ReadBinaryFile(path.c_str(), error);
if (!ret.has_value())
Error::AddPrefixFmt(error, "Failed to read resource file '{}': ", filename);
return ret;
}
std::optional<std::string> Host::ReadResourceFileToString(std::string_view filename, bool allow_override, Error* error)
{
const std::string path = QtHost::GetResourcePath(filename, allow_override);
return FileSystem::ReadFileToString(path.c_str(), error);
const std::optional<std::string> ret = FileSystem::ReadFileToString(path.c_str(), error);
if (!ret.has_value())
Error::AddPrefixFmt(error, "Failed to read resource file '{}': ", filename);
return ret;
}
std::optional<std::time_t> Host::GetResourceFileTimestamp(std::string_view filename, bool allow_override)

View File

@ -392,10 +392,6 @@ bool SaveGameSettings(SettingsInterface* sif, bool delete_if_empty);
/// Downloads the specified URL to the provided path.
bool DownloadFile(QWidget* parent, const QString& title, std::string url, const char* path);
/// Downloads the specified URL, and extracts the specified file from a zip to a provided path.
bool DownloadFileFromZip(QWidget* parent, const QString& title, std::string url, const char* zip_filename,
const char* output_path);
/// Thread-safe settings access.
void QueueSettingsSave();

View File

@ -47,22 +47,15 @@ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "About %1")
#endif
namespace QtHost {
struct GlyphInfo
struct FontOrderInfo
{
const char* language;
const char* imgui_font_name;
const char* imgui_font_url;
const char16_t* used_glyphs;
ImGuiManager::TextFontOrder font_order;
};
static QString FixLanguageName(const QString& language);
static void UpdateGlyphRangesAndClearCache(QWidget* dialog_parent, std::string_view language);
static bool DownloadMissingFont(QWidget* dialog_parent, const char* font_name, const char* font_url,
const std::string& path);
static const GlyphInfo* GetGlyphInfo(std::string_view language);
static constexpr const char* DEFAULT_IMGUI_FONT_NAME = "Roboto-Regular.ttf";
#define MAKE_FONT_DOWNLOAD_URL(name) "https://www.duckstation.org/runtime-resources/fonts/" name
static void UpdateFontOrder(std::string_view language);
static const FontOrderInfo* GetFontOrderInfo(std::string_view language);
static std::vector<QTranslator*> s_translators;
} // namespace QtHost
@ -77,8 +70,8 @@ void QtHost::UpdateApplicationLanguage(QWidget* dialog_parent)
s_translators.clear();
// Fix old language names.
const QString language =
FixLanguageName(QString::fromStdString(Host::GetBaseStringSettingValue("Main", "Language", GetDefaultLanguage())));
const std::string config_language = Host::GetBaseStringSettingValue("Main", "Language", GetDefaultLanguage());
const QString language = FixLanguageName(QString::fromStdString(config_language));
// install the base qt translation first
#ifndef __APPLE__
@ -142,7 +135,7 @@ void QtHost::UpdateApplicationLanguage(QWidget* dialog_parent)
s_translators.push_back(translator);
// We end up here both on language change, and on startup.
UpdateGlyphRangesAndClearCache(dialog_parent, language.toStdString());
UpdateFontOrder(config_language);
}
QString QtHost::FixLanguageName(const QString& language)
@ -240,161 +233,47 @@ const char* QtHost::GetDefaultLanguage()
return "en";
}
static constexpr const ImWchar s_base_latin_range[] = {
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x00B0, 0x00B0, // Degree sign
0x2022, 0x2022, // General punctuation
};
static constexpr const ImWchar s_central_european_ranges[] = {
0x0100, 0x017F, // Central European diacritics
};
void QtHost::UpdateGlyphRangesAndClearCache(QWidget* dialog_parent, std::string_view language)
void QtHost::UpdateFontOrder(std::string_view language)
{
const GlyphInfo* gi = GetGlyphInfo(language);
const char* imgui_font_name = nullptr;
const char* imgui_font_url = nullptr;
std::vector<ImWchar> glyph_ranges;
glyph_ranges.clear();
glyph_ranges.reserve(std::size(s_base_latin_range) + 2);
// Base Latin range is always included.
glyph_ranges.insert(glyph_ranges.end(), std::begin(s_base_latin_range), std::end(s_base_latin_range));
if (gi)
{
if (gi->used_glyphs)
{
const char16_t* ptr = gi->used_glyphs;
while (*ptr != 0)
{
// Always should be in pairs.
DebugAssert(ptr[0] != 0 && ptr[1] != 0);
glyph_ranges.push_back(*(ptr++));
glyph_ranges.push_back(*(ptr++));
}
}
imgui_font_name = gi->imgui_font_name;
imgui_font_url = gi->imgui_font_url;
}
// If we don't have any specific glyph range, assume Central European, except if English, then keep the size down.
if ((!gi || !gi->used_glyphs) && language != "en")
{
glyph_ranges.insert(glyph_ranges.end(), std::begin(s_central_european_ranges), std::end(s_central_european_ranges));
}
// List terminator.
glyph_ranges.push_back(0);
glyph_ranges.push_back(0);
// Check for the presence of font files.
std::string font_path;
if (imgui_font_name)
{
DebugAssert(imgui_font_url);
// Non-standard fonts always go to the user resources directory, since they're downloaded on demand.
font_path = Path::Combine(EmuFolders::UserResources,
SmallString::from_format("fonts" FS_OSPATH_SEPARATOR_STR "{}", imgui_font_name));
if (!DownloadMissingFont(dialog_parent, imgui_font_name, imgui_font_url, font_path))
font_path.clear();
}
if (font_path.empty())
{
// Use the default font.
font_path = EmuFolders::GetOverridableResourcePath(
SmallString::from_format("fonts" FS_OSPATH_SEPARATOR_STR "{}", DEFAULT_IMGUI_FONT_NAME));
}
ImGuiManager::TextFontOrder font_order = ImGuiManager::GetDefaultTextFontOrder();
if (const FontOrderInfo* fo = GetFontOrderInfo(language))
font_order = fo->font_order;
if (g_emu_thread)
{
Host::RunOnCPUThread([font_path = std::move(font_path), glyph_ranges = std::move(glyph_ranges)]() mutable {
GPUThread::RunOnThread([font_path = std::move(font_path), glyph_ranges = std::move(glyph_ranges)]() mutable {
ImGuiManager::SetFontPathAndRange(std::move(font_path), std::move(glyph_ranges));
});
Host::RunOnCPUThread([font_order]() mutable {
GPUThread::RunOnThread([font_order]() mutable { ImGuiManager::SetTextFontOrder(font_order); });
Host::ClearTranslationCache();
});
}
else
{
// Startup, safe to set directly.
ImGuiManager::SetFontPathAndRange(std::move(font_path), std::move(glyph_ranges));
ImGuiManager::SetTextFontOrder(font_order);
Host::ClearTranslationCache();
}
}
bool QtHost::DownloadMissingFont(QWidget* dialog_parent, const char* font_name, const char* font_url,
const std::string& path)
{
if (FileSystem::FileExists(path.c_str()))
return true;
#define TF(name) ImGuiManager::TextFont::name
{
QMessageBox msgbox(dialog_parent);
msgbox.setWindowTitle(qApp->translate("QtHost", "Missing Font File"));
msgbox.setWindowModality(Qt::WindowModal);
msgbox.setWindowIcon(QtHost::GetAppIcon());
msgbox.setIcon(QMessageBox::Critical);
msgbox.setTextFormat(Qt::RichText);
msgbox.setText(
qApp
->translate(
"QtHost",
"The font file '%1' is required for the On-Screen Display and Big Picture Mode to show messages in your "
"language.<br><br>"
"Do you want to download this file now? These files are usually less than 10 megabytes in size.<br><br>"
"<strong>If you do not download this file, on-screen messages will not be readable.</strong>")
.arg(QLatin1StringView(font_name)));
msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
if (msgbox.exec() != QMessageBox::Yes)
return false;
}
const QString progress_title = qApp->translate("QtHost", "Downloading Files");
if (StringUtil::EndsWithNoCase(font_url, ".zip"))
return QtHost::DownloadFileFromZip(dialog_parent, progress_title, font_url, font_name, path.c_str());
else
return QtHost::DownloadFile(dialog_parent, progress_title, font_url, path.c_str());
}
// clang-format off
static constexpr const char16_t s_cyrillic_ranges[] = {
/* Cyrillic + Cyrillic Supplement */ 0x0400, 0x052F, /* Extended-A */ 0x2DE0, 0x2DFF, /* Extended-B */ 0xA640, 0xA69F, 0, 0
// Why is this a thing? Because we want all glyphs to be available, but don't want to conflict
// between codepoints shared between Chinese and Japanese. Therefore we prioritize the language
// that the user has selected.
static constexpr const QtHost::FontOrderInfo s_font_order[] = {
{"ja", {TF(Default), TF(Japanese), TF(Chinese), TF(Korean)}},
{"ko", {TF(Default), TF(Korean), TF(Japanese), TF(Chinese)}},
{"zh-CN", {TF(Default), TF(Chinese), TF(Japanese), TF(Korean)}},
};
static constexpr const QtHost::GlyphInfo s_glyph_info[] = {
// Cyrillic languages
{ "ru", nullptr, nullptr, s_cyrillic_ranges },
{ "sr", nullptr, nullptr, s_cyrillic_ranges },
{ "uk", nullptr, nullptr, s_cyrillic_ranges },
{
"ja", "NotoSansJP-Regular.ttf", MAKE_FONT_DOWNLOAD_URL("NotoSansJP-Regular.zip"),
// auto update by generate_update_glyph_ranges.py with duckstation-qt_ja.ts
u"←↓□□△△○○ 。々々「」〜〜ああいいううええおせそそたちっばびびへべほもややゆゆよろわわをんァチッツテニネロワワンン・ー一一三下不与両両並並中中主主了了予予事二互互交交人人今介他他付付代以件件任任休休伸伸位低体体何何作作使使例例供供依依価価便便係係保保信信修修個個倍倍借借値値停停側側傍傍備備像像償償優優元元先光入入全全公公共共具典内内再再凍凍処処出出分切初初判別利利到到制制削削前前割割力力加加効効動動勧勧化化十十協協単単原原去去参参及及反反取取古古可可右右号号各各合合同名向向含含告告周周命命品品商商問問善善回回因因困困囲囲固固国国圧在地地垂垂型型埋埋域域基基報報場場境境増増壊壊声声売売変変外外多多大大央央失失奥奥奨奨妙妙妥妥始始子子字存学学守安完完定宛実実容容密密対対専専射射導小少少履履岐岐左左差差巻巻帰帰常常幅幅平年度座延延式式引引弱弱張張強強当当形形影影役役待待後後従従得得御御復復微微心心必必忘忘応応性性恐恐悪悪情情意意感感態態成我戻戻所所手手扱扱技技投投択択押押拡拡持持指指振振挿挿捗捗排排探探接接推推描提換換揮揮損損摩摩撃撃撮撮操操改改敗敗数数整整文文料料断断新新方方既既日日早早明明昔昔映映昨昨時時景景更更書書替最有有望望期期未未本本来来析析枚枚果果枠枠栄栄棄棄検検楽楽概概構構標標権権機機欄欄欠次止正歪歪歴歴残残毎毎比比民民水水永永求求汎汎決決況況法法波波注注海海消消深深混混済済減減測測満満源源準準滑滑演演点点無無照照版版物物牲牲特特犠犠状状献献率率現現理理生生用用由由申申画画界界番番異異疑疑発登的的目目直直瞬瞬知知短短破破確確示示禁禁秒秒移移程程種種穴穴空空立立端端符符等等策策算算管管範範簡簡粋粋精精約約純純素素索索細細終終組組結結統統続続維維緑緑線線編編縦縦縮縮績績繰繰置置者者耗耗背背能能自自致致般般良良色色荷荷落落行行術術表表装装補補製製複複要要見見規規視視覚覚覧覧観観角角解解言言計計記記設設許許訳訳証証試試詳詳認認語語説読調調識識警警象象負負貢貢販販貫貫費費質質赤赤起起超超跡跡転転軸軸軽軽較較輪輪込込近近返返追追送送逆逆透透通通速速連連進進遅遅遊遊達達遠遠適適遷選避避部部郭郭重量録録長長閉閉開開間間関関防防降降限限除除隅隅際際隠隠集集離難電電青青非非面面音音響響頂頂順順領領頭頭頻頼題題類類飛飛験験高高黒黒%%()55::??XX"
},
{
"ko", "NotoSansKR-Regular.ttf", MAKE_FONT_DOWNLOAD_URL("NotoSansKR-Regular.zip"),
// auto update by generate_update_glyph_ranges.py with duckstation-qt_ko.ts
u"“”™™←↓□□△△○○◯◯。。んんイイジジメメーー茶茶가각간간감값갔강같같개객갱갱거거건건걸걸검겁것것게게겠겠겨격견견결결경경계계고곡곤곤곳곳공공과곽관관괴괴교교구국굴굴권권귀귀규규그그근근글글금급기기긴긴길길김깁깅깅깊깊까까깜깝깨깨꺼꺼께께꼭꼭꼴꼴꽂꽂꾸꾸꿈꿈끄끄끈끈끊끊끌끌끔끔끝끝나나난난날날낮낮내내낼낼너너널널넘넘넣네넷넷노노높놓누누눈눈눌눌뉴뉴느느는는늘늘능능니니닌닌님닙다닥단단닫달담담당당대대댑댑더더던던덜덜덤덤덮덮데덱델델뎁뎁도독돌돌동동되되된된될될됨됩두두둔둔둘둘둥둥뒤뒤듀듀드득든든들들듭듭등등디디딩딩따따때때떠떠떤떤떨떨떻떻또또뚜뚜뛰뛰뛸뛸뜁뜁뜨뜨뜻뜻띄띄라락란란랍랍랑랑래랙랜랜램랩랫랫량량러럭런런럼럽렀렀렇렉렌렌려력련련렬렬렷렷령령로록론론롤롤롬롭롯롯료료루루룹룹류류률률륨륨르르른른를를름릅리릭린린릴릴림립릿릿링링마막만만많많말말맞맞매매머머먼먼멀멀멈멈멋멋멍멍메메멘멘며며면면명명몇몇모목못못묘묘무무문문물물뭉뭉뮬뮬므므미미밀밀밍밍및및바밖반반받밝방방배백밴밴버벅번번벌벌범법벗벗베베벤벤벨벨벳벳변변별별병병보복본본볼볼봉봉부부분분불불뷰뷰브브블블비빅빈빈빌빌빗빗빛빛빠빠빨빨뿐뿐사삭산산삼삽상상새색샘샘생생샤샤샷샷서석선선설설성성세섹센센셀셀셈셈셋셋션션셰셰소속손손솔솔송송쇼쇼수수순순술술숨숨숫숫쉽쉽슈슈스스슬슬습습승승시식신신실실심십싱싱싶싶쌍쌍써써썰썰쓰쓰씁씁씌씌씬씬아악안안않않알알암압았앙앞앞애액앤앤앨앨앱앱앵앵야약양양어어언언얻얼업없었었에에엔엔여역연연열열염염영영예예오오온온올올옵옵와와완완왑왑왔왔왜왜외외왼왼요요용용우욱운운움웁웃웃워워원원웨웨위위유유윤윤율율으으은은을을음음응응의의이이인인일읽임입있있잊잊자작잘잘잠잡장장재재잿잿저적전전절절점접정정제젝젠젠젯젯져져졌졌조족존존종종좋좌죄죄주주준준줄줄줍줍중중즈즉즐즐즘즘증증지직진진질질짐집짜짜째째쪽쪽찍찍차착참참창찾채책챌챌처처천천청청체체쳐쳐초초총총촬촬최최추축춘춘출출충충춰춰취취츠측치치칠칠침침카카캐캐캔캔캠캡커커컨컨컬컬컴컴케케켜켜켤켤켬켬코코콘콘콜콜쿼쿼퀀퀀큐큐크크큰큰클클큼큼키키킬킬킵킵킹킹타타탄탄탐탐태택탬탭터터턴턴털털테텍텐텐템텝토토톱톱통통투투트특튼튼틀틀틈틈티틱틴틴틸틸팅팅파파판판팔팔팝팝패패퍼퍼페페편편평평폐폐포폭폴폴폼폼표표푸푸풀풀품품퓨퓨프프픈픈플플피픽필필핑핑하학한한할할함합핫핫항항해핵했행향향허허헌헌험험헤헤현현형형호혹혼혼화확환환활활황황회획횟횟횡횡효효후후훨훨휘휘휠휠휴휴흐흐흔흔희희히히힘힙XX"
},
{
"zh-CN", "NotoSansSC-Regular.ttf", MAKE_FONT_DOWNLOAD_URL("NotoSansSC-Regular.zip"),
// auto update by generate_update_glyph_ranges.py with duckstation-qt_zh-cn.ts
u"​​——“”……、。一丁三下不与专且世世丢丢两两个个中中串串临临为主么义之之乐乐乘乘也也了了事二于于互互五五亚些交交产产亮亮人人仅仅今介仍从仓仓他他付付代以们们件价任任份份仿仿休休众优会会传传伤伤伴伴伸伸但但位住体体何何作作佳佳使使例例供供依依侧侧便便保保信信修修倍倍倒倒候候借借值值假假偏偏做做停停储储像像允允元元充兆先光免免入入全全公六共共关关其典兼兼内内册再冗冗写写冲决况况冻冻准准减减几几凭凭出击函函分切划划列列则则创创初初删删利利别别到到制刷前前剔剔剪剪副副力力功务动助勾勾包包化化匹区十十升升半半协协卓卓单单南南占卡即即历历压压原原去去参参又及双反发发取变叠叠口口另另只只可台史右号司各各合合同后向向吗吗否否含听启启呈呈告告员员周周味味命命和和咫咫哈哈响响哪哪唯唯商商善善器器噪噪四四回回因因困困围围固固国图圆圆圈圈在在地地场场址址均均坏坐块块垂垂型型域域基基堆堆填填增增声声处处备备复复外外多多夜夜够够大大天太失失头头夹夹奏奏奖套好好如如妙妙始始娱娱媒媒子子孔孔字存它它安安完完宏宏官官定定宝实客客家家容容宽宽寄寄密密察察寸对寻导寿寿封封射射将将小小少少尚尚尝尝尤尤就就尺尺尼尽局局层层屏屏展展属属峰峰崩崩工左巨巨差差己已希希帐帐帜帜带帧帮帮常常幅幅幕幕平年并并序序库底度度廓廓延延建建开开异弃式式引引张张弦弦弱弱弹强当当录录形形彩彩影影彼彼往往径待很很律律得得循循微微心心必忆志志快快忽忽态态急急性性总总恢恢息息您您情情惯惯想想愉愉意意感感慢慢憾憾戏我或或战战截截戳戳户户所所扇扇手手才才打打托托执执扩扩扫扭批批找技投折护报抱抱抹抹担拆拉拉拟拟拥拥择择括括拾拿持挂指指按按挑挑振振捉捉捕捕损损换换据据掉掉掌掌排排接接控掩描提插插握握搜搜携携摇摇摘摘摸摸撤撤播播操擎支支改改放放故故效效敏敏数数整整文文料料斜斜断断新新方方旋旋旗旗无无日旧早早时时明明星映昨昨是是显显晚晚景景暂暂暗暗曜曜曲曲更更替最有有服服望望期期未本术术机机权权杆杆束条来来杯杯松板构构析析果果枪枪架架柄柄某某染染查查栅栅标栈栏栏树树校校样根格格框框案案桌桌档档械械梳梳检检概概榜榜模模橇橇次欢欧欧歉歉止步死死段段母母每每比比毫毫水水求求汇汇池污没没法法注注洲洲活活流流浅浅测测浏浏浮浮消消涡涡深深混混添添清清渐渐渠渡渲渲游游溃溃源源滑滑滚滚滞滞滤滤演演潜潜澳澳激激灰灰灵灵点点烁烁热热焦焦然然照照片版牌牌牙牙物物特特状状独独献献率率玩玩环现理理瓶瓶生生用用由由电电画画畅畅界界留留略略登登白百的的监盒盖盖盘盘目目直直相相省省看看真眠着着睡睡瞄瞄瞬瞬知知矩矩矫矫石石码码破破础础硬硬确确磁磁示示禁禁离离种种秒秒称称移移程程稍稍稳稳空空穿穿突突窗窗立立端端符符第第等等筛筛签签简简算算管管类类粉粉精精糊糊系系素素索索紧紧紫紫繁繁红红约级纯纯纵纵纹纹线线组组细终经经绑绑结结绕绕绘给络络统统继继续续维维绿绿缓缓编编缘缘缠缠缩缩缺缺网网置置美美翻翻者者而而耐耐联联肩肩背背胜胜能能脑脑脚脚自自至致舍舍航航般般色色节节若若范范荐荐获获菜菜著著蓝蓝藏藏虚虚融融行行补补表表衷衷被被裁裂装装要要覆覆见观规规视视览觉角角解解触触言言警警计订认认议议记记许许论论设访证证评评识识译译试试话话该详语语误误说说请诸读读调调负负贡贡败账质质贴贴费费资资赖赖起起超超越越足足距距跟跟跨跨路路跳跳踪踪身身车车轨轨转转轮软轴轴载载较较辑辑输输辨辨边边达达过过迎迎运近返返还这进远连迟述述追追退适逆逆选选透逐递递通通速速遇遇道道遗遗遥遥避避那那邻邻部部都都配配醒醒采采释释里量金金针针钟钟钮钮钴钴链链销锁锐锐错错键锯镜镜长长闪闪闭问闲闲间间阈阈队队防防阴阴阶阶阻阻附际降降限限除除险险隆隆随隐隔隔障障隶隶难难集集雨雨需需震震静静非非靠靠面面音音页顶项须顿顿预预颈颈频频题题颜额颠颠风风饱饱馈馈驱驱验验骤骤高高鸭鸭黄黄黑黑默默鼠鼠齐齐齿齿!!%%,,::??"
},
};
// clang-format on
#undef TF
const QtHost::GlyphInfo* QtHost::GetGlyphInfo(std::string_view language)
const QtHost::FontOrderInfo* QtHost::GetFontOrderInfo(std::string_view language)
{
for (const GlyphInfo& it : s_glyph_info)
for (const FontOrderInfo& it : s_font_order)
{
if (language == it.language)
return &it;
}
return nullptr;
}
}

View File

@ -984,7 +984,6 @@ private:
void CloseShaderCache();
bool CreateResources(Error* error);
void DestroyResources();
static bool IsTexturePoolType(GPUTexture::Type type);
static size_t s_total_vram_usage;

View File

@ -265,10 +265,9 @@ static UIState s_state;
} // namespace ImGuiFullscreen
void ImGuiFullscreen::SetFonts(ImFont* medium_font, ImFont* large_font)
void ImGuiFullscreen::SetFont(ImFont* ui_font)
{
UIStyle.MediumFont = medium_font;
UIStyle.LargeFont = large_font;
UIStyle.Font = ui_font;
}
bool ImGuiFullscreen::Initialize(const char* placeholder_image_path)
@ -296,8 +295,7 @@ void ImGuiFullscreen::Shutdown(bool clear_state)
s_state.initialized = false;
s_state.texture_upload_queue.clear();
s_state.placeholder_texture.reset();
UIStyle.MediumFont = nullptr;
UIStyle.LargeFont = nullptr;
UIStyle.Font = nullptr;
s_state.texture_cache.Clear();
@ -599,6 +597,8 @@ bool ImGuiFullscreen::UpdateLayoutScale()
}
UIStyle.RcpLayoutScale = 1.0f / UIStyle.LayoutScale;
UIStyle.LargeFontSize = LayoutScale(LAYOUT_LARGE_FONT_SIZE);
UIStyle.MediumFontSize = LayoutScale(LAYOUT_MEDIUM_FONT_SIZE);
return (UIStyle.LayoutScale != old_scale);
@ -610,6 +610,8 @@ bool ImGuiFullscreen::UpdateLayoutScale()
const float old_scale = UIStyle.LayoutScale;
UIStyle.LayoutScale = std::max(io.DisplaySize.x, io.DisplaySize.y) / LAYOUT_SCREEN_WIDTH;
UIStyle.RcpLayoutScale = 1.0f / UIStyle.LayoutScale;
UIStyle.LargeFontSize = LayoutScale(LAYOUT_LARGE_FONT_SIZE);
UIStyle.MediumFontSize = LayoutScale(LAYOUT_MEDIUM_FONT_SIZE);
return (UIStyle.LayoutScale != old_scale);
#endif
@ -972,10 +974,10 @@ bool ImGuiFullscreen::BeginFullscreenColumns(const char* title, float pos_y, boo
bool clipped;
if (title)
{
ImGui::PushFont(UIStyle.LargeFont);
ImGui::PushFontSize(UIStyle.LargeFontSize, UIStyle.BoldFontWeight);
clipped = ImGui::Begin(title, nullptr,
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground);
ImGui::PopFont();
ImGui::PopFontSize();
}
else
{
@ -1166,7 +1168,9 @@ void ImGuiFullscreen::DrawFullscreenFooter()
dl->AddRectFilled(ImVec2(0.0f, io.DisplaySize.y - height), io.DisplaySize,
ImGui::GetColorU32(ModAlpha(UIStyle.PrimaryColor, s_state.fullscreen_text_alpha)), 0.0f);
ImFont* const font = UIStyle.MediumFont;
ImFont* const font = UIStyle.Font;
const float font_size = UIStyle.MediumFontSize;
const float font_weight = UIStyle.BoldFontWeight;
const float max_width = io.DisplaySize.x - padding * 2.0f;
float prev_opacity = 0.0f;
@ -1186,22 +1190,22 @@ void ImGuiFullscreen::DrawFullscreenFooter()
{
if (!s_state.last_fullscreen_footer_text.empty())
{
const ImVec2 text_size =
font->CalcTextSizeA(font->FontSize, max_width, 0.0f, IMSTR_START_END(s_state.last_fullscreen_footer_text));
const ImVec2 text_size = font->CalcTextSizeA(font_size, font_weight, max_width, 0.0f,
IMSTR_START_END(s_state.last_fullscreen_footer_text));
const ImVec2 text_pos =
ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding);
dl->AddText(font, font->FontSize, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, prev_opacity),
ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font_size - padding);
dl->AddText(font, font_size, font_weight, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, prev_opacity),
IMSTR_START_END(s_state.last_fullscreen_footer_text));
dl->AddText(font, font->FontSize, text_pos, ModAlpha(text_color, prev_opacity),
dl->AddText(font, font_size, font_weight, text_pos, ModAlpha(text_color, prev_opacity),
IMSTR_START_END(s_state.last_fullscreen_footer_text));
}
if (!s_state.last_left_fullscreen_footer_text.empty())
{
const ImVec2 text_pos = ImVec2(padding, io.DisplaySize.y - font->FontSize - padding);
dl->AddText(font, font->FontSize, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, prev_opacity),
const ImVec2 text_pos = ImVec2(padding, io.DisplaySize.y - font_size - padding);
dl->AddText(font, font_size, font_weight, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, prev_opacity),
IMSTR_START_END(s_state.last_left_fullscreen_footer_text));
dl->AddText(font, font->FontSize, text_pos, ModAlpha(text_color, prev_opacity),
dl->AddText(font, font_size, font_weight, text_pos, ModAlpha(text_color, prev_opacity),
IMSTR_START_END(s_state.last_left_fullscreen_footer_text));
}
}
@ -1216,23 +1220,23 @@ void ImGuiFullscreen::DrawFullscreenFooter()
if (!s_state.fullscreen_footer_text.empty())
{
const ImVec2 text_size =
font->CalcTextSizeA(font->FontSize, max_width, 0.0f, IMSTR_START_END(s_state.fullscreen_footer_text));
font->CalcTextSizeA(font_size, font_weight, max_width, 0.0f, IMSTR_START_END(s_state.fullscreen_footer_text));
const ImVec2 text_pos =
ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding);
ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font_size - padding);
const float opacity = 1.0f - prev_opacity;
dl->AddText(font, font->FontSize, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, opacity),
dl->AddText(font, font_size, font_weight, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, opacity),
IMSTR_START_END(s_state.fullscreen_footer_text));
dl->AddText(font, font->FontSize, text_pos, ModAlpha(text_color, opacity),
dl->AddText(font, font_size, font_weight, text_pos, ModAlpha(text_color, opacity),
IMSTR_START_END(s_state.fullscreen_footer_text));
}
if (!s_state.left_fullscreen_footer_text.empty())
{
const ImVec2 text_pos = ImVec2(padding, io.DisplaySize.y - font->FontSize - padding);
const ImVec2 text_pos = ImVec2(padding, io.DisplaySize.y - font_size - padding);
const float opacity = 1.0f - prev_opacity;
dl->AddText(font, font->FontSize, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, opacity),
dl->AddText(font, font_size, font_weight, text_pos + shadow_offset, MulAlpha(UIStyle.ShadowColor, opacity),
IMSTR_START_END(s_state.left_fullscreen_footer_text));
dl->AddText(font, font->FontSize, text_pos, ModAlpha(text_color, opacity),
dl->AddText(font, font_size, font_weight, text_pos, ModAlpha(text_color, opacity),
IMSTR_START_END(s_state.left_fullscreen_footer_text));
}
}
@ -1322,18 +1326,18 @@ void ImGuiFullscreen::DrawWindowTitle(std::string_view title)
ImGuiWindow* window = ImGui::GetCurrentWindow();
const ImVec2 pos(window->DC.CursorPos + LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING));
const ImVec2 size(window->WorkRect.GetWidth() - (LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING) * 2.0f),
UIStyle.LargeFont->FontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f);
UIStyle.LargeFontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f);
const ImRect rect(pos, pos + size);
ImGui::ItemSize(size);
if (!ImGui::ItemAdd(rect, window->GetID("window_title")))
return;
ImGui::PushFont(UIStyle.LargeFont);
ImGui::PushFont(UIStyle.Font, UIStyle.LargeFontSize);
ImGui::RenderTextClipped(rect.Min, rect.Max, IMSTR_START_END(title), nullptr, ImVec2(0.0f, 0.0f), &rect);
ImGui::PopFont();
const ImVec2 line_start(pos.x, pos.y + UIStyle.LargeFont->FontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING));
const ImVec2 line_start(pos.x, pos.y + UIStyle.LargeFontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING));
const ImVec2 line_end(pos.x + size.x, line_start.y);
const float line_thickness = LayoutScale(1.0f);
ImDrawList* dl = ImGui::GetWindowDrawList();
@ -1474,10 +1478,11 @@ void ImGuiFullscreen::ResetMenuButtonFrame()
s_state.has_hovered_menu_item = false;
}
void ImGuiFullscreen::RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, const ImVec2& pos_min,
const ImVec2& pos_max, u32 color, std::string_view text,
const ImVec2* text_size_if_known, const ImVec2& align, float wrap_width,
const ImRect* clip_rect, float shadow_offset)
void ImGuiFullscreen::RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight,
const ImVec2& pos_min, const ImVec2& pos_max, u32 color,
std::string_view text, const ImVec2* text_size_if_known,
const ImVec2& align, float wrap_width, const ImRect* clip_rect,
float shadow_offset)
{
if (text.empty())
return;
@ -1493,7 +1498,7 @@ void ImGuiFullscreen::RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* f
ImVec2 pos = pos_min;
const ImVec2 text_size = text_size_if_known ?
*text_size_if_known :
font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(text), nullptr);
font->CalcTextSizeA(font_size, font_weight, FLT_MAX, 0.0f, IMSTR_START_END(text), nullptr);
const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
@ -1517,35 +1522,36 @@ void ImGuiFullscreen::RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* f
if (need_clipping)
{
ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
draw_list->AddText(font, font->FontSize, ImVec2(pos.x + shadow_offset, pos.y + shadow_offset), shadow_color,
draw_list->AddText(font, font_size, font_weight, ImVec2(pos.x + shadow_offset, pos.y + shadow_offset), shadow_color,
IMSTR_START_END(text), wrap_width, &fine_clip_rect);
draw_list->AddText(font, font->FontSize, pos, color, IMSTR_START_END(text), wrap_width, &fine_clip_rect);
draw_list->AddText(font, font_size, font_weight, pos, color, IMSTR_START_END(text), wrap_width, &fine_clip_rect);
}
else
{
draw_list->AddText(font, font->FontSize, ImVec2(pos.x + shadow_offset, pos.y + shadow_offset), shadow_color,
draw_list->AddText(font, font_size, font_weight, ImVec2(pos.x + shadow_offset, pos.y + shadow_offset), shadow_color,
IMSTR_START_END(text), wrap_width, nullptr);
draw_list->AddText(font, font->FontSize, pos, color, IMSTR_START_END(text), wrap_width, nullptr);
draw_list->AddText(font, font_size, font_weight, pos, color, IMSTR_START_END(text), wrap_width, nullptr);
}
}
void ImGuiFullscreen::RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, const ImVec2& pos_min,
void ImGuiFullscreen::RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight,
const ImVec2& pos_min, const ImVec2& pos_max, u32 color,
std::string_view text, const ImVec2* text_size_if_known /* = nullptr */,
const ImVec2& align /* = ImVec2(0, 0)*/, float wrap_width /* = 0.0f*/,
const ImRect* clip_rect /* = nullptr */)
{
RenderShadowedTextClipped(draw_list, font, font_size, font_weight, pos_min, pos_max, color, text, text_size_if_known,
align, wrap_width, clip_rect, LayoutScale(LAYOUT_SHADOW_OFFSET));
}
void ImGuiFullscreen::RenderShadowedTextClipped(ImFont* font, float font_size, float font_weight, const ImVec2& pos_min,
const ImVec2& pos_max, u32 color, std::string_view text,
const ImVec2* text_size_if_known /* = nullptr */,
const ImVec2& align /* = ImVec2(0, 0)*/, float wrap_width /* = 0.0f*/,
const ImRect* clip_rect /* = nullptr */)
{
RenderShadowedTextClipped(draw_list, font, pos_min, pos_max, color, text, text_size_if_known, align, wrap_width,
clip_rect, LayoutScale(LAYOUT_SHADOW_OFFSET));
}
void ImGuiFullscreen::RenderShadowedTextClipped(ImFont* font, const ImVec2& pos_min, const ImVec2& pos_max, u32 color,
std::string_view text, const ImVec2* text_size_if_known /* = nullptr */,
const ImVec2& align /* = ImVec2(0, 0)*/, float wrap_width /* = 0.0f*/,
const ImRect* clip_rect /* = nullptr */)
{
RenderShadowedTextClipped(ImGui::GetWindowDrawList(), font, pos_min, pos_max, color, text, text_size_if_known, align,
wrap_width, clip_rect);
RenderShadowedTextClipped(ImGui::GetWindowDrawList(), font, font_size, font_weight, pos_min, pos_max, color, text,
text_size_if_known, align, wrap_width, clip_rect);
}
void ImGuiFullscreen::MenuHeading(std::string_view title, bool draw_line /*= true*/)
@ -1559,12 +1565,12 @@ void ImGuiFullscreen::MenuHeading(std::string_view title, bool draw_line /*= tru
if (!visible)
return;
RenderShadowedTextClipped(UIStyle.LargeFont, bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_TextDisabled), title,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max,
ImGui::GetColorU32(ImGuiCol_TextDisabled), title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb);
if (draw_line)
{
const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding);
const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFontSize + line_padding);
const ImVec2 line_end(bb.Max.x, line_start.y);
const ImVec2 shadow_offset = LayoutScale(LAYOUT_SHADOW_OFFSET, LAYOUT_SHADOW_OFFSET);
ImGui::GetWindowDrawList()->AddLine(line_start + shadow_offset, line_end + shadow_offset, UIStyle.ShadowColor,
@ -1587,20 +1593,21 @@ bool ImGuiFullscreen::MenuHeadingButton(std::string_view title, std::string_view
return false;
const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled);
RenderShadowedTextClipped(UIStyle.LargeFont, bb.Min, bb.Max, color, title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max, color, title,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb);
if (!value.empty())
{
const ImVec2 value_size(UIStyle.LargeFont->CalcTextSizeA(
UIStyle.LargeFont->FontSize, std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(value)));
const ImVec2 value_size(UIStyle.Font->CalcTextSizeA(
UIStyle.LargeFontSize, UIStyle.BoldFontWeight, std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(value)));
const ImRect value_bb(ImVec2(bb.Max.x - value_size.x, bb.Min.y), ImVec2(bb.Max.x, bb.Max.y));
RenderShadowedTextClipped(UIStyle.LargeFont, value_bb.Min, value_bb.Max, color, value, &value_size,
ImVec2(0.0f, 0.0f), 0.0f, &value_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, value_bb.Min, value_bb.Max,
color, value, &value_size, ImVec2(0.0f, 0.0f), 0.0f, &value_bb);
}
if (draw_line)
{
const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding);
const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFontSize + line_padding);
const ImVec2 line_end(bb.Max.x, line_start.y);
const ImVec2 shadow_offset = LayoutScale(LAYOUT_SHADOW_OFFSET, LAYOUT_SHADOW_OFFSET);
ImGui::GetWindowDrawList()->AddLine(line_start + shadow_offset, line_end + shadow_offset, UIStyle.ShadowColor,
@ -1612,8 +1619,9 @@ bool ImGuiFullscreen::MenuHeadingButton(std::string_view title, std::string_view
return pressed;
}
bool ImGuiFullscreen::MenuButton(std::string_view title, std::string_view summary, bool enabled, float height,
ImFont* font, ImFont* summary_font, const ImVec2& text_align /*= ImVec2(0.0f, 0.0f)*/)
bool ImGuiFullscreen::MenuButton(std::string_view title, std::string_view summary, bool enabled /* = true */,
float height /* = LAYOUT_MENU_BUTTON_HEIGHT */,
const ImVec2& text_align /* = ImVec2(0.0f, 0.0f) */)
{
ImRect bb;
bool visible, hovered;
@ -1621,34 +1629,36 @@ bool ImGuiFullscreen::MenuButton(std::string_view title, std::string_view summar
if (!visible)
return false;
const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint));
const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled];
RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(color), title, nullptr, text_align,
0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(color), title, nullptr, text_align, 0.0f, &title_bb);
if (!summary.empty())
{
const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max);
RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)),
summary, nullptr, text_align, 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, nullptr, text_align,
0.0f, &summary_bb);
}
s_state.menu_button_index++;
return pressed;
}
bool ImGuiFullscreen::MenuButtonWithoutSummary(std::string_view title, bool enabled /*= true*/,
float height /*= LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY*/,
ImFont* font /*= UIStyle.LargeFont*/,
const ImVec2& text_align /*= ImVec2(0.0f, 0.0f)*/)
bool ImGuiFullscreen::MenuButtonWithoutSummary(std::string_view title, bool enabled /* = true */,
float height, /* = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY */
const ImVec2& text_align /* = ImVec2(0.0f, 0.0f) */)
{
return MenuButton(title, {}, enabled, height, font, nullptr, text_align);
return MenuButton(title, {}, enabled, height, text_align);
}
bool ImGuiFullscreen::MenuImageButton(std::string_view title, std::string_view summary, ImTextureID user_texture_id,
const ImVec2& image_size, bool enabled, float height, const ImVec2& uv0,
const ImVec2& uv1, ImFont* title_font, ImFont* summary_font)
const ImVec2& image_size, bool enabled /* = true */,
float height /* = LAYOUT_MENU_BUTTON_HEIGHT */,
const ImVec2& uv0 /* = ImVec2(0.0f, 0.0f) */,
const ImVec2& uv1 /* = ImVec2(1.0f, 1.0f) */)
{
ImRect bb;
bool visible, hovered;
@ -1660,30 +1670,33 @@ bool ImGuiFullscreen::MenuImageButton(std::string_view title, std::string_view s
enabled ? IM_COL32(255, 255, 255, 255) :
ImGui::GetColorU32(ImGuiCol_TextDisabled));
const float midpoint = bb.Min.y + title_font->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max);
const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled];
RenderShadowedTextClipped(title_font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(color), title, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(color), title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
if (!summary.empty())
{
RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)),
summary, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
}
s_state.menu_button_index++;
return pressed;
}
bool ImGuiFullscreen::FloatingButton(std::string_view text, float x, float y, float width, float height, float anchor_x,
float anchor_y, bool enabled, ImFont* font, ImVec2* out_position,
bool repeat_button)
bool ImGuiFullscreen::FloatingButton(std::string_view text, float x, float y, float width /* = -1.0f */,
float height /* = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY */,
float anchor_x /* = 0.0f */, float anchor_y /* = 0.0f */,
bool enabled /* = true */, ImVec2* out_position /* = nullptr */,
bool repeat_button /* = false */)
{
const ImVec2 text_size(
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(text)));
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight,
std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(text));
const ImVec2& padding(ImGui::GetStyle().FramePadding);
if (width < 0.0f)
width = (padding.x * 2.0f) + text_size.x;
@ -1756,12 +1769,13 @@ bool ImGuiFullscreen::FloatingButton(std::string_view text, float x, float y, fl
bb.Max -= padding;
const u32 color = enabled ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(ImGuiCol_TextDisabled);
RenderShadowedTextClipped(font, bb.Min, bb.Max, color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max, color, text,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb);
return pressed;
}
bool ImGuiFullscreen::ToggleButton(std::string_view title, std::string_view summary, bool* v, bool enabled,
float height, ImFont* font, ImFont* summary_font)
bool ImGuiFullscreen::ToggleButton(std::string_view title, std::string_view summary, bool* v, bool enabled /* = true */,
float height /* = LAYOUT_MENU_BUTTON_HEIGHT */)
{
ImRect bb;
bool visible, hovered;
@ -1769,19 +1783,20 @@ bool ImGuiFullscreen::ToggleButton(std::string_view title, std::string_view summ
if (!visible)
return false;
const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint));
const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max);
const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled];
RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(color), title, nullptr,
ImVec2(0.0, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(color), title, nullptr, ImVec2(0.0, 0.0f), 0.0f, &title_bb);
if (!summary.empty())
{
RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)),
summary, nullptr, ImVec2(0.0, 0.0f), 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, nullptr,
ImVec2(0.0, 0.0f), 0.0f, &summary_bb);
}
const float toggle_width = LayoutScale(50.0f);
@ -1829,7 +1844,7 @@ bool ImGuiFullscreen::ToggleButton(std::string_view title, std::string_view summ
}
bool ImGuiFullscreen::ThreeWayToggleButton(std::string_view title, std::string_view summary, std::optional<bool>* v,
bool enabled, float height, ImFont* font, ImFont* summary_font)
bool enabled /* = true */, float height /* = LAYOUT_MENU_BUTTON_HEIGHT */)
{
ImRect bb;
bool visible, hovered;
@ -1837,19 +1852,20 @@ bool ImGuiFullscreen::ThreeWayToggleButton(std::string_view title, std::string_v
if (!visible)
return false;
const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint));
const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max);
const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled];
RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(color), title, nullptr,
ImVec2(0.0, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(color), title, nullptr, ImVec2(0.0, 0.0f), 0.0f, &title_bb);
if (!summary.empty())
{
RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)),
summary, nullptr, ImVec2(0.0, 0.0f), 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, nullptr,
ImVec2(0.0, 0.0f), 0.0f, &summary_bb);
}
const float toggle_width = LayoutScale(50.0f);
@ -1902,9 +1918,8 @@ bool ImGuiFullscreen::ThreeWayToggleButton(std::string_view title, std::string_v
}
bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summary, s32* value, s32 min, s32 max,
s32 increment, const char* format, bool enabled /*= true*/,
float height /*= LAYOUT_MENU_BUTTON_HEIGHT*/, ImFont* font /*= g_large_font*/,
ImFont* summary_font /*= g_medium_font*/, std::string_view ok_text /*= "OK"*/)
s32 increment, const char* format /* = "%d" */, bool enabled /* = true */,
float height /* = LAYOUT_MENU_BUTTON_HEIGHT */, std::string_view ok_text /* = "OK" */)
{
ImRect bb;
bool visible, hovered;
@ -1913,22 +1928,24 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa
return false;
SmallString value_text = SmallString::from_sprintf(format, *value);
const ImVec2 value_size = font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(value_text));
const ImVec2 value_size = UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight, FLT_MAX, 0.0f,
IMSTR_START_END(value_text));
const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const float text_end = bb.Max.x - value_size.x;
const ImRect title_bb(bb.Min, ImVec2(text_end, midpoint));
const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), ImVec2(text_end, bb.Max.y));
const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled];
RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(color), title, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(font, bb.Min, bb.Max, ImGui::GetColorU32(color), value_text, &value_size,
ImVec2(1.0f, 0.5f), 0.0f, &bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(color), title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max,
ImGui::GetColorU32(color), value_text, &value_size, ImVec2(1.0f, 0.5f), 0.0f, &bb);
if (!summary.empty())
{
RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)),
summary, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
}
if (pressed)
@ -1953,8 +1970,7 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa
ImGui::PopStyleVar(2);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f)))
if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f)))
{
CloseFixedPopupDialog();
}
@ -1968,9 +1984,8 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa
}
bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summary, float* value, float min, float max,
float increment, const char* format, bool enabled /*= true*/,
float height /*= LAYOUT_MENU_BUTTON_HEIGHT*/, ImFont* font /*= g_large_font*/,
ImFont* summary_font /*= g_medium_font*/, std::string_view ok_text /*= "OK"*/)
float increment, const char* format /* = "%f" */, bool enabled /* = true */,
float height /* = LAYOUT_MENU_BUTTON_HEIGHT */, std::string_view ok_text /* = "OK" */)
{
ImRect bb;
bool visible, hovered;
@ -1979,22 +1994,24 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa
return false;
SmallString value_text = SmallString::from_sprintf(format, *value);
const ImVec2 value_size = font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, IMSTR_START_END(value_text));
const ImVec2 value_size = UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight, FLT_MAX, 0.0f,
IMSTR_START_END(value_text));
const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const float text_end = bb.Max.x - value_size.x;
const ImRect title_bb(bb.Min, ImVec2(text_end, midpoint));
const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), ImVec2(text_end, bb.Max.y));
const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled];
RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(color), title, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(font, bb.Min, bb.Max, ImGui::GetColorU32(color), value_text, &value_size,
ImVec2(1.0f, 0.5f), 0.0f, &bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(color), title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max,
ImGui::GetColorU32(color), value_text, &value_size, ImVec2(1.0f, 0.5f), 0.0f, &bb);
if (!summary.empty())
{
RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)),
summary, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
}
if (pressed)
@ -2018,8 +2035,7 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa
ImGui::PopStyleVar(2);
if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f)))
if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImVec2(0.5f, 0.0f)))
{
CloseFixedPopupDialog();
}
@ -2033,7 +2049,7 @@ bool ImGuiFullscreen::RangeButton(std::string_view title, std::string_view summa
}
bool ImGuiFullscreen::MenuButtonWithValue(std::string_view title, std::string_view summary, std::string_view value,
bool enabled, float height, ImFont* font, ImFont* summary_font)
bool enabled /* = true */, float height /* = LAYOUT_MENU_BUTTON_HEIGHT */)
{
ImRect bb;
bool visible, hovered;
@ -2041,22 +2057,24 @@ bool ImGuiFullscreen::MenuButtonWithValue(std::string_view title, std::string_vi
if (!visible)
return false;
const ImVec2 value_size = ImGui::CalcTextSize(IMSTR_START_END(value));
const ImVec2 value_size =
UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight, FLT_MAX, 0.0f, IMSTR_START_END(value));
const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f);
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + LayoutScale(4.0f);
const float text_end = bb.Max.x - value_size.x;
const ImRect title_bb(bb.Min, ImVec2(text_end, midpoint));
const ImVec4& color = ImGui::GetStyle().Colors[enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled];
RenderShadowedTextClipped(font, title_bb.Min, title_bb.Max, ImGui::GetColorU32(color), title, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(font, bb.Min, bb.Max, ImGui::GetColorU32(color), value, nullptr, ImVec2(1.0f, 0.5f), 0.0f,
&bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(color), title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max,
ImGui::GetColorU32(color), value, nullptr, ImVec2(1.0f, 0.5f), 0.0f, &bb);
if (!summary.empty())
{
const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), ImVec2(text_end, bb.Max.y));
RenderShadowedTextClipped(summary_font, summary_bb.Min, summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)),
summary, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
summary_bb.Max, ImGui::GetColorU32(DarkerColor(color)), summary, nullptr,
ImVec2(0.0f, 0.0f), 0.0f, &summary_bb);
}
return pressed;
@ -2064,11 +2082,10 @@ bool ImGuiFullscreen::MenuButtonWithValue(std::string_view title, std::string_vi
bool ImGuiFullscreen::EnumChoiceButtonImpl(std::string_view title, std::string_view summary, s32* value_pointer,
const char* (*to_display_name_function)(s32 value, void* opaque),
void* opaque, u32 count, bool enabled, float height, ImFont* font,
ImFont* summary_font)
void* opaque, u32 count, bool enabled, float height)
{
const bool pressed = MenuButtonWithValue(title, summary, to_display_name_function(*value_pointer, opaque), enabled,
height, font, summary_font);
const bool pressed =
MenuButtonWithValue(title, summary, to_display_name_function(*value_pointer, opaque), enabled, height);
if (pressed)
{
@ -2123,8 +2140,7 @@ void ImGuiFullscreen::EndNavBar()
ImGui::PopStyleVar(4);
}
void ImGuiFullscreen::NavTitle(std::string_view title, float height /*= LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY*/,
ImFont* font /*= g_large_font*/)
void ImGuiFullscreen::NavTitle(std::string_view title, float height /* = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY */)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
@ -2132,8 +2148,8 @@ void ImGuiFullscreen::NavTitle(std::string_view title, float height /*= LAYOUT_M
s_state.menu_button_index++;
const ImVec2 text_size(
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(title)));
const ImVec2 text_size(UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight,
std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(title)));
const ImVec2 pos(window->DC.CursorPos);
const ImGuiStyle& style = ImGui::GetStyle();
const ImVec2 size = ImVec2(text_size.x, LayoutScale(height) + style.FramePadding.y * 2.0f);
@ -2149,8 +2165,8 @@ void ImGuiFullscreen::NavTitle(std::string_view title, float height /*= LAYOUT_M
bb.Min.y += style.FramePadding.y;
bb.Max.y -= style.FramePadding.y;
RenderShadowedTextClipped(font, bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Text), title, &text_size,
ImVec2(0.0f, 0.0f), 0.0f, &bb);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max,
ImGui::GetColorU32(ImGuiCol_Text), title, &text_size, ImVec2(0.0f, 0.0f), 0.0f, &bb);
}
void ImGuiFullscreen::RightAlignNavButtons(u32 num_items /*= 0*/,
@ -2167,8 +2183,7 @@ void ImGuiFullscreen::RightAlignNavButtons(u32 num_items /*= 0*/,
}
bool ImGuiFullscreen::NavButton(std::string_view title, bool is_active, bool enabled /* = true */,
float width /* = -1.0f */, float height /* = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY */,
ImFont* font /* = g_large_font */)
float width /* = -1.0f */, float height /* = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY */)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
@ -2176,8 +2191,8 @@ bool ImGuiFullscreen::NavButton(std::string_view title, bool is_active, bool ena
s_state.menu_button_index++;
const ImVec2 text_size(
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(title)));
const ImVec2 text_size(UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight,
std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(title)));
const ImVec2 pos(window->DC.CursorPos);
const ImGuiStyle& style = ImGui::GetStyle();
const ImVec2 size = ImVec2(((width < 0.0f) ? text_size.x : LayoutScale(width)) + style.FramePadding.x * 2.0f,
@ -2225,15 +2240,15 @@ bool ImGuiFullscreen::NavButton(std::string_view title, bool is_active, bool ena
bb.Max -= style.FramePadding;
RenderShadowedTextClipped(
font, bb.Min, bb.Max,
UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max,
ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered), title,
&text_size, ImVec2(0.0f, 0.0f), 0.0f, &bb);
return pressed;
}
bool ImGuiFullscreen::NavTab(std::string_view title, bool is_active, bool enabled /* = true */, float width,
float height, const ImVec4& background, ImFont* font /* = g_large_font */)
bool ImGuiFullscreen::NavTab(std::string_view title, bool is_active, bool enabled, float width, float height,
const ImVec4& background)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
@ -2241,8 +2256,8 @@ bool ImGuiFullscreen::NavTab(std::string_view title, bool is_active, bool enable
s_state.menu_button_index++;
const ImVec2 text_size(
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(title)));
const ImVec2 text_size(UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight,
std::numeric_limits<float>::max(), 0.0f, IMSTR_START_END(title)));
const ImVec2 pos(window->DC.CursorPos);
const ImVec2 size = ImVec2(((width < 0.0f) ? text_size.x : LayoutScale(width)), LayoutScale(height));
@ -2300,7 +2315,7 @@ bool ImGuiFullscreen::NavTab(std::string_view title, bool is_active, bool enable
bb.Max -= pad;
RenderShadowedTextClipped(
font, bb.Min, bb.Max,
UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, bb.Min, bb.Max,
ImGui::GetColorU32(enabled ? (is_active ? ImGuiCol_Text : ImGuiCol_TextDisabled) : ImGuiCol_ButtonHovered), title,
nullptr, ImVec2(0.0f, 0.0f), 0.0f, &bb);
@ -2382,26 +2397,30 @@ bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, std::string_view titl
dl->AddImage(reinterpret_cast<ImTextureID>(icon), icon_box.Min, icon_box.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
color);
ImFont* title_font = UIStyle.LargeFont;
const ImVec2 title_size = title_font->CalcTextSizeA(title_font->FontSize, std::numeric_limits<float>::max(),
avail_width, IMSTR_START_END(title));
ImFont* const title_font = UIStyle.Font;
const float title_font_size = UIStyle.LargeFontSize;
const float title_font_weight = UIStyle.BoldFontWeight;
const ImVec2 title_size = title_font->CalcTextSizeA(
title_font_size, title_font_weight, std::numeric_limits<float>::max(), avail_width, IMSTR_START_END(title));
const ImVec2 title_pos =
ImVec2(bb.Min.x + (avail_width - title_size.x) * 0.5f, icon_pos.y + icon_size + LayoutScale(10.0f));
const ImRect title_bb = ImRect(title_pos, title_pos + title_size);
RenderShadowedTextClipped(title_font, title_bb.Min, title_bb.Max,
RenderShadowedTextClipped(title_font, title_font_size, title_font_weight, title_bb.Min, title_bb.Max,
ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), title, &title_size,
ImVec2(0.0f, 0.0f), avail_width, &title_bb);
if (!description.empty())
{
ImFont* desc_font = UIStyle.MediumFont;
const ImVec2 desc_size = desc_font->CalcTextSizeA(desc_font->FontSize, std::numeric_limits<float>::max(),
avail_width, IMSTR_START_END(description));
ImFont* const desc_font = UIStyle.Font;
const float desc_font_size = UIStyle.MediumFontSize;
const float desc_font_weight = UIStyle.NormalFontWeight;
const ImVec2 desc_size = desc_font->CalcTextSizeA(
desc_font_size, desc_font_weight, std::numeric_limits<float>::max(), avail_width, IMSTR_START_END(description));
const ImVec2 desc_pos = ImVec2(bb.Min.x + (avail_width - desc_size.x) * 0.5f, title_bb.Max.y + LayoutScale(10.0f));
const ImRect desc_bb = ImRect(desc_pos, desc_pos + desc_size);
RenderShadowedTextClipped(desc_font, desc_bb.Min, desc_bb.Max,
RenderShadowedTextClipped(desc_font, desc_font_size, desc_font_weight, desc_bb.Min, desc_bb.Max,
ImGui::GetColorU32(DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text])), description,
nullptr, ImVec2(0.0f, 0.0f), avail_width, &desc_bb);
}
@ -2534,7 +2553,7 @@ bool ImGuiFullscreen::PopupDialog::BeginRender(float scaled_window_padding /* =
}
}
ImGui::PushFont(UIStyle.LargeFont);
ImGui::PushFont(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, scaled_window_rounding);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(scaled_window_padding, scaled_window_padding));
@ -2586,6 +2605,8 @@ bool ImGuiFullscreen::PopupDialog::BeginRender(float scaled_window_padding /* =
// don't draw unreadable text
ImGui::PopStyleColor(1);
ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor);
ImGui::PopFont();
ImGui::PushFont(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.NormalFontWeight);
if (WantsToCloseMenu())
StartClose();
@ -2829,7 +2850,7 @@ void ImGuiFullscreen::ChoiceDialog::Draw()
const float width = LayoutScale(600.0f);
const float title_height =
UIStyle.LargeFont->FontSize + (LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) + (LayoutScale(10.0f) * 2.0f);
UIStyle.LargeFontSize + (LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) + (LayoutScale(10.0f) * 2.0f);
const float item_height =
(LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + (LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) +
LayoutScale(LAYOUT_MENU_BUTTON_SPACING));
@ -3286,9 +3307,9 @@ void ImGuiFullscreen::DrawBackgroundProgressDialogs(ImVec2& position, float spac
IM_COL32(0x11, 0x11, 0x11, 200), LayoutScale(10.0f));
ImVec2 pos(window_pos_x + LayoutScale(10.0f), window_pos_y + LayoutScale(10.0f));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, pos, IM_COL32(255, 255, 255, 255),
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, pos, IM_COL32(255, 255, 255, 255),
IMSTR_START_END(data.message), 0.0f);
pos.y += UIStyle.MediumFont->FontSize + LayoutScale(10.0f);
pos.y += UIStyle.MediumFontSize + LayoutScale(10.0f);
const ImVec2 box_end(pos.x + window_width - LayoutScale(10.0f * 2.0f), pos.y + LayoutScale(25.0f));
dl->AddRectFilled(pos, box_end, ImGui::GetColorU32(UIStyle.PrimaryDarkColor));
@ -3300,10 +3321,11 @@ void ImGuiFullscreen::DrawBackgroundProgressDialogs(ImVec2& position, float spac
ImGui::GetColorU32(UIStyle.SecondaryColor));
TinyString text = TinyString::from_format("{}%", static_cast<int>(std::round(fraction * 100.0f)));
const ImVec2 text_size(ImGui::CalcTextSize(text));
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
0.0f, IMSTR_START_END(text));
const ImVec2 text_pos(pos.x + ((box_end.x - pos.x) / 2.0f) - (text_size.x / 2.0f),
pos.y + ((box_end.y - pos.y) / 2.0f) - (text_size.y / 2.0f));
dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos,
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, text_pos,
ImGui::GetColorU32(UIStyle.PrimaryTextColor), IMSTR_START_END(text));
}
else
@ -3414,7 +3436,7 @@ void ImGuiFullscreen::DrawLoadingScreen(std::string_view image, std::string_view
const float padding_and_rounding = 18.0f * scale;
const float frame_rounding = 6.0f * scale;
const float bar_height = ImCeil(ImGuiManager::GetOSDFont()->FontSize * 1.1f);
const float bar_height = ImCeil(ImGuiManager::GetOSDFontSize() * 1.1f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, padding_and_rounding);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(padding_and_rounding, padding_and_rounding));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, frame_rounding);
@ -3422,7 +3444,7 @@ void ImGuiFullscreen::DrawLoadingScreen(std::string_view image, std::string_view
ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor);
ImGui::PushStyleColor(ImGuiCol_FrameBg, UIStyle.BackgroundColor);
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, UIStyle.SecondaryColor);
ImGui::PushFont(ImGuiManager::GetOSDFont());
ImGui::PushFont(ImGuiManager::GetTextFont(), ImGuiManager::GetOSDFontSize());
ImGui::SetNextWindowSize(ImVec2(width, ((has_progress || is_persistent) ? 85.0f : 55.0f) * scale), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, (io.DisplaySize.y * 0.5f) + (100.0f * scale)),
ImGuiCond_Always, ImVec2(0.5f, 0.0f));
@ -3546,8 +3568,12 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
const float shadow_size = ImGuiFullscreen::LayoutScale(2.0f);
const float rounding = ImGuiFullscreen::LayoutScale(20.0f);
ImFont* const title_font = ImGuiFullscreen::UIStyle.LargeFont;
ImFont* const text_font = ImGuiFullscreen::UIStyle.MediumFont;
ImFont* const title_font = UIStyle.Font;
const float title_font_size = UIStyle.LargeFontSize;
const float title_font_weight = UIStyle.BoldFontWeight;
ImFont* const text_font = UIStyle.Font;
const float text_font_size = UIStyle.MediumFontSize;
const float text_font_weight = UIStyle.NormalFontWeight;
for (u32 index = 0; index < static_cast<u32>(s_state.notifications.size());)
{
@ -3559,10 +3585,10 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
continue;
}
const ImVec2 title_size =
title_font->CalcTextSizeA(title_font->FontSize, max_text_width, max_text_width, IMSTR_START_END(notif.title));
const ImVec2 text_size =
text_font->CalcTextSizeA(text_font->FontSize, max_text_width, max_text_width, IMSTR_START_END(notif.text));
const ImVec2 title_size = title_font->CalcTextSizeA(title_font_size, title_font_weight, max_text_width,
max_text_width, IMSTR_START_END(notif.title));
const ImVec2 text_size = text_font->CalcTextSizeA(text_font_size, text_font_weight, max_text_width, max_text_width,
IMSTR_START_END(notif.text));
float box_width = std::max((horizontal_padding * 2.0f) + badge_size + horizontal_spacing +
ImCeil(std::max(title_size.x, text_size.x)),
@ -3641,13 +3667,13 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
const ImVec2 title_pos = ImVec2(badge_max.x + horizontal_spacing, box_min.y + vertical_padding);
const ImRect title_bb = ImRect(title_pos, title_pos + title_size);
RenderShadowedTextClipped(dl, title_font, title_bb.Min, title_bb.Max, title_col, notif.title, &title_size,
ImVec2(0.0f, 0.0f), max_text_width, &title_bb);
RenderShadowedTextClipped(dl, title_font, title_font_size, title_font_weight, title_bb.Min, title_bb.Max, title_col,
notif.title, &title_size, ImVec2(0.0f, 0.0f), max_text_width, &title_bb);
const ImVec2 text_pos = ImVec2(badge_max.x + horizontal_spacing, title_bb.Max.y + vertical_spacing);
const ImRect text_bb = ImRect(text_pos, text_pos + text_size);
RenderShadowedTextClipped(dl, text_font, text_bb.Min, text_bb.Max, text_col, notif.text, &text_size,
ImVec2(0.0f, 0.0f), max_text_width, &text_bb);
RenderShadowedTextClipped(dl, text_font, text_font_size, text_font_weight, text_bb.Min, text_bb.Max, text_col,
notif.text, &text_size, ImVec2(0.0f, 0.0f), max_text_width, &text_bb);
if (clip_box)
dl->PopClipRect();
@ -3702,21 +3728,25 @@ void ImGuiFullscreen::DrawToast()
const float max_width = LayoutScale(600.0f);
ImFont* title_font = UIStyle.LargeFont;
ImFont* message_font = UIStyle.MediumFont;
ImFont* const title_font = UIStyle.Font;
const float title_font_size = UIStyle.LargeFontSize;
const float title_font_weight = UIStyle.BoldFontWeight;
ImFont* message_font = UIStyle.Font;
const float message_font_size = UIStyle.MediumFontSize;
const float message_font_weight = UIStyle.NormalFontWeight;
const float padding = LayoutScale(20.0f);
const float total_padding = padding * 2.0f;
const float margin = LayoutScale(20.0f + (s_state.fullscreen_footer_text.empty() ? 0.0f : LAYOUT_FOOTER_HEIGHT));
const float spacing = s_state.toast_title.empty() ? 0.0f : LayoutScale(10.0f);
const ImVec2 display_size = ImGui::GetIO().DisplaySize;
const ImVec2 title_size =
s_state.toast_title.empty() ?
ImVec2(0.0f, 0.0f) :
title_font->CalcTextSizeA(title_font->FontSize, FLT_MAX, max_width, IMSTR_START_END(s_state.toast_title));
const ImVec2 message_size =
s_state.toast_message.empty() ?
ImVec2(0.0f, 0.0f) :
message_font->CalcTextSizeA(message_font->FontSize, FLT_MAX, max_width, IMSTR_START_END(s_state.toast_message));
const ImVec2 title_size = s_state.toast_title.empty() ?
ImVec2(0.0f, 0.0f) :
title_font->CalcTextSizeA(title_font_size, title_font_weight, FLT_MAX, max_width,
IMSTR_START_END(s_state.toast_title));
const ImVec2 message_size = s_state.toast_message.empty() ?
ImVec2(0.0f, 0.0f) :
message_font->CalcTextSizeA(message_font_size, message_font_weight, FLT_MAX, max_width,
IMSTR_START_END(s_state.toast_message));
const ImVec2 comb_size(std::max(title_size.x, message_size.x), title_size.y + spacing + message_size.y);
const ImVec2 box_size(comb_size.x + total_padding, comb_size.y + total_padding);
@ -3732,8 +3762,8 @@ void ImGuiFullscreen::DrawToast()
const float offset = (comb_size.x - title_size.x) * 0.5f;
const ImVec2 title_pos = box_pos + ImVec2(offset + padding, padding);
const ImRect title_bb = ImRect(title_pos, title_pos + title_size);
RenderShadowedTextClipped(dl, title_font, title_bb.Min, title_bb.Max, text_col, s_state.toast_title, &title_size,
ImVec2(0.0f, 0.0f), max_width, &title_bb);
RenderShadowedTextClipped(dl, title_font, title_font_size, title_font_weight, title_bb.Min, title_bb.Max, text_col,
s_state.toast_title, &title_size, ImVec2(0.0f, 0.0f), max_width, &title_bb);
}
if (!s_state.toast_message.empty())
{
@ -3742,8 +3772,9 @@ void ImGuiFullscreen::DrawToast()
const float offset = (comb_size.x - message_size.x) * 0.5f;
const ImVec2 message_pos = box_pos + ImVec2(offset + padding, padding + spacing + title_size.y);
const ImRect message_bb = ImRect(message_pos, message_pos + message_size);
RenderShadowedTextClipped(dl, message_font, message_bb.Min, message_bb.Max, text_col, s_state.toast_message,
&message_size, ImVec2(0.0f, 0.0f), max_width, &message_bb);
RenderShadowedTextClipped(dl, message_font, message_font_size, message_font_weight, message_bb.Min, message_bb.Max,
text_col, s_state.toast_message, &message_size, ImVec2(0.0f, 0.0f), max_width,
&message_bb);
}
}

View File

@ -77,8 +77,7 @@ struct ALIGN_TO_CACHE_LINE UIStyles
ImVec4 ToastBackgroundColor;
ImVec4 ToastTextColor;
ImFont* MediumFont;
ImFont* LargeFont;
ImFont* Font;
u32 ShadowColor;
@ -86,6 +85,11 @@ struct ALIGN_TO_CACHE_LINE UIStyles
float RcpLayoutScale;
float LayoutPaddingLeft;
float LayoutPaddingTop;
float LargeFontSize;
float MediumFontSize;
static constexpr float NormalFontWeight = 0.0f;
static constexpr float BoldFontWeight = 500.0f;
bool Animations;
bool SmoothScrolling;
@ -180,7 +184,7 @@ void SetTheme(std::string_view theme);
void SetAnimations(bool enabled);
void SetSmoothScrolling(bool enabled);
void SetMenuBorders(bool enabled);
void SetFonts(ImFont* medium_font, ImFont* large_font);
void SetFont(ImFont* ui_font);
bool UpdateLayoutScale();
/// Shuts down, clearing all state.
@ -278,59 +282,50 @@ bool MenuButtonFrame(std::string_view str_id, bool enabled, float height, bool*
ImVec2* max, ImGuiButtonFlags flags = 0, float hover_alpha = 1.0f);
void DrawMenuButtonFrame(const ImVec2& p_min, const ImVec2& p_max, ImU32 fill_col, bool border = true);
void ResetMenuButtonFrame();
void RenderShadowedTextClipped(ImFont* font, const ImVec2& pos_min, const ImVec2& pos_max, u32 color,
void RenderShadowedTextClipped(ImFont* font, float font_size, float font_weight, const ImVec2& pos_min, const ImVec2& pos_max, u32 color,
std::string_view text, const ImVec2* text_size_if_known = nullptr,
const ImVec2& align = ImVec2(0, 0), float wrap_width = 0.0f,
const ImRect* clip_rect = nullptr);
void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, const ImVec2& pos_min, const ImVec2& pos_max,
u32 color, std::string_view text, const ImVec2* text_size_if_known = nullptr,
const ImVec2& align = ImVec2(0, 0), float wrap_width = 0.0f,
const ImRect* clip_rect = nullptr);
void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, const ImVec2& pos_min, const ImVec2& pos_max,
u32 color, std::string_view text, const ImVec2* text_size_if_known, const ImVec2& align,
float wrap_width, const ImRect* clip_rect, float shadow_offset);
void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight, const ImVec2& pos_min,
const ImVec2& pos_max, u32 color, std::string_view text,
const ImVec2* text_size_if_known = nullptr, const ImVec2& align = ImVec2(0, 0),
float wrap_width = 0.0f, const ImRect* clip_rect = nullptr);
void RenderShadowedTextClipped(ImDrawList* draw_list, ImFont* font, float font_size, float font_weight, const ImVec2& pos_min,
const ImVec2& pos_max, u32 color, std::string_view text,
const ImVec2* text_size_if_known, const ImVec2& align, float wrap_width,
const ImRect* clip_rect, float shadow_offset);
void MenuHeading(std::string_view title, bool draw_line = true);
bool MenuHeadingButton(std::string_view title, std::string_view value = {}, bool enabled = true, bool draw_line = true);
bool MenuButton(std::string_view title, std::string_view summary, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = UIStyle.LargeFont,
ImFont* summary_font = UIStyle.MediumFont, const ImVec2& text_align = ImVec2(0.0f, 0.0f));
float height = LAYOUT_MENU_BUTTON_HEIGHT, const ImVec2& text_align = ImVec2(0.0f, 0.0f));
bool MenuButtonWithoutSummary(std::string_view title, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont,
const ImVec2& text_align = ImVec2(0.0f, 0.0f));
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, const ImVec2& text_align = ImVec2(0.0f, 0.0f));
bool MenuButtonWithValue(std::string_view title, std::string_view summary, std::string_view value, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = UIStyle.LargeFont,
ImFont* summary_font = UIStyle.MediumFont);
float height = LAYOUT_MENU_BUTTON_HEIGHT);
bool MenuImageButton(std::string_view title, std::string_view summary, ImTextureID user_texture_id,
const ImVec2& image_size, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
const ImVec2& uv0 = ImVec2(0.0f, 0.0f), const ImVec2& uv1 = ImVec2(1.0f, 1.0f),
ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont);
const ImVec2& uv0 = ImVec2(0.0f, 0.0f), const ImVec2& uv1 = ImVec2(1.0f, 1.0f));
bool FloatingButton(std::string_view text, float x, float y, float width = -1.0f,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, float anchor_x = 0.0f, float anchor_y = 0.0f,
bool enabled = true, ImFont* font = UIStyle.LargeFont, ImVec2* out_position = nullptr,
bool repeat_button = false);
bool enabled = true, ImVec2* out_position = nullptr, bool repeat_button = false);
bool ToggleButton(std::string_view title, std::string_view summary, bool* v, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = UIStyle.LargeFont,
ImFont* summary_font = UIStyle.MediumFont);
float height = LAYOUT_MENU_BUTTON_HEIGHT);
bool ThreeWayToggleButton(std::string_view title, std::string_view summary, std::optional<bool>* v, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = UIStyle.LargeFont,
ImFont* summary_font = UIStyle.MediumFont);
float height = LAYOUT_MENU_BUTTON_HEIGHT);
bool RangeButton(std::string_view title, std::string_view summary, s32* value, s32 min, s32 max, s32 increment,
const char* format = "%d", bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont,
std::string_view ok_text = "OK");
bool RangeButton(std::string_view title, std::string_view summary, float* value, float min, float max, float increment,
const char* format = "%f", bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont,
std::string_view ok_text = "OK");
bool EnumChoiceButtonImpl(std::string_view title, std::string_view summary, s32* value_pointer,
const char* (*to_display_name_function)(s32 value, void* opaque), void* opaque, u32 count,
bool enabled, float height, ImFont* font, ImFont* summary_font);
bool enabled, float height);
template<typename DataType, typename CountType>
ALWAYS_INLINE static bool EnumChoiceButton(std::string_view title, std::string_view summary, DataType* value_pointer,
const char* (*to_display_name_function)(DataType value), CountType count,
bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont)
bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT)
{
s32 value = static_cast<s32>(*value_pointer);
auto to_display_name_wrapper = [](s32 value, void* opaque) -> const char* {
@ -338,7 +333,7 @@ ALWAYS_INLINE static bool EnumChoiceButton(std::string_view title, std::string_v
};
if (EnumChoiceButtonImpl(title, summary, &value, to_display_name_wrapper, &to_display_name_function,
static_cast<u32>(count), enabled, height, font, summary_font))
static_cast<u32>(count), enabled, height))
{
*value_pointer = static_cast<DataType>(value);
return true;
@ -351,14 +346,12 @@ ALWAYS_INLINE static bool EnumChoiceButton(std::string_view title, std::string_v
void BeginNavBar(float x_padding = LAYOUT_MENU_BUTTON_X_PADDING, float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING);
void EndNavBar();
void NavTitle(std::string_view title, float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
ImFont* font = UIStyle.LargeFont);
void NavTitle(std::string_view title, float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
void RightAlignNavButtons(u32 num_items = 0, float item_width = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
float item_height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
bool NavButton(std::string_view title, bool is_active, bool enabled = true, float width = -1.0f,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont);
bool NavTab(std::string_view title, bool is_active, bool enabled, float width, float height, const ImVec4& background,
ImFont* font = UIStyle.LargeFont);
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
bool NavTab(std::string_view title, bool is_active, bool enabled, float width, float height, const ImVec4& background);
bool BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, const ImVec4& bg_color,
u32 num_items);

View File

@ -1,8 +0,0 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
static constexpr ImWchar FA_ICON_RANGE[] = { 0xe06f,0xe070,0xe086,0xe086,0xf002,0xf002,0xf005,0xf005,0xf007,0xf007,0xf00c,0xf00e,0xf011,0xf013,0xf017,0xf017,0xf019,0xf019,0xf01c,0xf01c,0xf021,0xf023,0xf025,0xf026,0xf028,0xf028,0xf02e,0xf02e,0xf030,0xf030,0xf03a,0xf03a,0xf03d,0xf03e,0xf04a,0xf04c,0xf050,0xf050,0xf056,0xf056,0xf059,0xf059,0xf05e,0xf05e,0xf062,0xf063,0xf065,0xf067,0xf071,0xf071,0xf075,0xf075,0xf077,0xf078,0xf07b,0xf07c,0xf083,0xf085,0xf091,0xf091,0xf09c,0xf09c,0xf0ac,0xf0ae,0xf0b2,0xf0b2,0xf0c3,0xf0c3,0xf0c5,0xf0c5,0xf0c7,0xf0c9,0xf0cb,0xf0cb,0xf0d0,0xf0d0,0xf0dc,0xf0dc,0xf0e0,0xf0e0,0xf0e2,0xf0e2,0xf0e7,0xf0e8,0xf0eb,0xf0eb,0xf0f1,0xf0f1,0xf0f3,0xf0f3,0xf0fe,0xf0fe,0xf110,0xf110,0xf11b,0xf11c,0xf140,0xf140,0xf144,0xf144,0xf146,0xf146,0xf14a,0xf14a,0xf15b,0xf15d,0xf191,0xf192,0xf1ab,0xf1ab,0xf1c0,0xf1c0,0xf1c5,0xf1c5,0xf1de,0xf1de,0xf1e6,0xf1e6,0xf1eb,0xf1eb,0xf1f8,0xf1f8,0xf1fb,0xf1fc,0xf201,0xf201,0xf240,0xf240,0xf242,0xf242,0xf245,0xf245,0xf252,0xf252,0xf26c,0xf26c,0xf279,0xf279,0xf2c1,0xf2c1,0xf2d0,0xf2d0,0xf2d2,0xf2d2,0xf2db,0xf2db,0xf2f1,0xf2f2,0xf302,0xf302,0xf31e,0xf31e,0xf35d,0xf35d,0xf360,0xf360,0xf362,0xf362,0xf3c1,0xf3c1,0xf3fd,0xf3fd,0xf410,0xf410,0xf422,0xf422,0xf424,0xf424,0xf462,0xf462,0xf466,0xf466,0xf4ce,0xf4ce,0xf500,0xf500,0xf517,0xf517,0xf51f,0xf51f,0xf538,0xf538,0xf545,0xf545,0xf547,0xf548,0xf54c,0xf54c,0xf55b,0xf55b,0xf55d,0xf55d,0xf565,0xf565,0xf56e,0xf570,0xf5a2,0xf5a2,0xf5aa,0xf5aa,0xf5ae,0xf5ae,0xf5c7,0xf5c7,0xf5cb,0xf5cb,0xf5e7,0xf5e7,0xf5ee,0xf5ee,0xf61f,0xf61f,0xf65d,0xf65e,0xf6a9,0xf6a9,0xf6cf,0xf6cf,0xf70c,0xf70c,0xf70e,0xf70e,0xf78c,0xf78c,0xf794,0xf794,0xf7a0,0xf7a0,0xf7a4,0xf7a5,0xf7c2,0xf7c2,0xf807,0xf807,0xf815,0xf815,0xf818,0xf818,0xf84c,0xf84c,0xf853,0xf853,0xf87d,0xf87d,0xf8cc,0xf8cc,0x0,0x0 };
static constexpr ImWchar PF_ICON_RANGE[] = { 0x2196,0x2199,0x219e,0x21a3,0x21b0,0x21b3,0x21ba,0x21c3,0x21c7,0x21ca,0x21d0,0x21d4,0x21e0,0x21e3,0x21e6,0x21e8,0x21eb,0x21eb,0x21ed,0x21ee,0x21f7,0x21f8,0x21fa,0x21fb,0x221a,0x221b,0x227a,0x227f,0x2284,0x2284,0x22bf,0x22c8,0x2349,0x2349,0x235e,0x235e,0x2360,0x2361,0x2364,0x2366,0x23b2,0x23b4,0x23cc,0x23cc,0x23ce,0x23ce,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243e,0x2446,0x2446,0x2460,0x246b,0x248f,0x248f,0x24f5,0x24fd,0x24ff,0x24ff,0x2717,0x2717,0x2753,0x2753,0x278a,0x278e,0x27fc,0x27fc,0xe000,0xe001,0xff21,0xff3a,0x1f52b,0x1f52b,0x0,0x0 };
static constexpr ImWchar EMOJI_ICON_RANGE[] = { 0x2139,0x2139,0x23e9,0x23ea,0x23f8,0x23f8,0x26a0,0x26a0,0x1f4be,0x1f4be,0x1f4c2,0x1f4c2,0x1f4f7,0x1f4f8,0x1f504,0x1f504,0x1f507,0x1f507,0x1f509,0x1f50a,0x1f50d,0x1f50d,0x1f513,0x1f513,0x0,0x0 };

View File

@ -6,7 +6,6 @@
#include "host.h"
#include "image.h"
#include "imgui_fullscreen.h"
#include "imgui_glyph_ranges.inl"
#include "input_manager.h"
#include "shadergen.h"
@ -41,6 +40,11 @@
LOG_CHANNEL(ImGuiManager);
// TODO: for dynamic fonts
// max texture size
// lock/bake osd font
// gc fonts on scale change
namespace ImGuiManager {
namespace {
@ -76,13 +80,10 @@ static void SetStyle(ImGuiStyle& style, float scale);
static void SetKeyMap();
static bool LoadFontData(Error* error);
static void ReloadFontDataIfActive();
static bool AddImGuiFonts(bool debug_font, bool fullscreen_fonts);
static ImFont* AddTextFont(float size, const ImWchar* glyph_range);
static ImFont* AddFixedFont(float size);
static bool AddIconFonts(float size, const ImWchar* emoji_range);
static bool CreateFontAtlas(Error* error);
static bool CompilePipelines(Error* error);
static void RenderDrawLists(u32 window_width, u32 window_height, WindowInfo::PreRotation prerotation);
static bool UpdateImGuiFontTexture();
static void UpdateTextures();
static void SetCommonIOOptions(ImGuiIO& io);
static void SetImKeyState(ImGuiIO& io, ImGuiKey imkey, bool pressed);
static const char* GetClipboardTextImpl(void* userdata);
@ -100,8 +101,12 @@ static void DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair<float,
static constexpr float OSD_FADE_IN_TIME = 0.1f;
static constexpr float OSD_FADE_OUT_TIME = 0.4f;
static constexpr std::array<ImWchar, 4> ASCII_FONT_RANGE = {{0x20, 0x7F, 0x00, 0x00}};
static constexpr std::array<ImWchar, 6> DEFAULT_FONT_RANGE = {{0x0020, 0x00FF, 0x2022, 0x2022, 0x0000, 0x0000}};
static constexpr std::array<const char*, static_cast<size_t>(TextFont::MaxCount)> TEXT_FONT_NAMES = {{
"Roboto-VariableFont_wdth,wght.ttf", // Default
"NotoSansSC-VariableFont_wght.ttf", // Chinese
"NotoSansJP-VariableFont_wght.ttf", // Japanese
"NotoSansKR-VariableFont_wght.ttf", // Korean
}};
namespace {
@ -133,13 +138,9 @@ struct ALIGN_TO_CACHE_LINE State
std::array<s8, 2> left_stick_axis_state = {};
std::unique_ptr<GPUPipeline> imgui_pipeline;
std::unique_ptr<GPUTexture> imgui_font_texture;
ImFont* debug_font = nullptr;
ImFont* osd_font = nullptr;
ImFont* text_font = nullptr;
ImFont* fixed_font = nullptr;
ImFont* medium_font = nullptr;
ImFont* large_font = nullptr;
std::deque<OSDMessage> osd_active_messages;
@ -148,12 +149,9 @@ struct ALIGN_TO_CACHE_LINE State
// mapping of host key -> imgui key
ALIGN_TO_CACHE_LINE std::unordered_map<u32, ImGuiKey> imgui_key_map;
std::string font_path;
std::vector<WCharType> font_range;
std::vector<WCharType> dynamic_font_range;
std::vector<WCharType> dynamic_emoji_range;
TextFontOrder text_font_order = {};
DynamicHeapArray<u8> standard_font_data;
std::array<DynamicHeapArray<u8>, static_cast<size_t>(TextFont::MaxCount)> text_fonts_data;
DynamicHeapArray<u8> fixed_font_data;
DynamicHeapArray<u8> icon_fa_font_data;
DynamicHeapArray<u8> icon_pf_font_data;
@ -166,59 +164,20 @@ static State s_state;
} // namespace ImGuiManager
void ImGuiManager::SetFontPathAndRange(std::string path, std::vector<WCharType> range)
ImGuiManager::TextFontOrder ImGuiManager::GetDefaultTextFontOrder()
{
if (s_state.font_path == path && s_state.font_range == range)
return;
s_state.font_path = std::move(path);
s_state.font_range = std::move(range);
s_state.standard_font_data = {};
ReloadFontDataIfActive();
return {TextFont::Default, TextFont::Japanese, TextFont::Chinese, TextFont::Korean};
}
void ImGuiManager::SetDynamicFontRange(std::vector<WCharType> font_range, std::vector<WCharType> emoji_range)
void ImGuiManager::SetTextFontOrder(const TextFontOrder& order)
{
if (s_state.dynamic_font_range == font_range && s_state.dynamic_emoji_range == emoji_range)
if (s_state.text_font_order == order)
return;
s_state.dynamic_font_range = std::move(font_range);
s_state.dynamic_emoji_range = std::move(emoji_range);
s_state.text_font_order = order;
ReloadFontDataIfActive();
}
std::vector<ImGuiManager::WCharType> ImGuiManager::CompactFontRange(std::span<const WCharType> range)
{
std::vector<ImWchar> ret;
for (auto it = range.begin(); it != range.end();)
{
auto next_it = it;
++next_it;
// Combine sequential ranges.
const ImWchar start_codepoint = *it;
ImWchar end_codepoint = start_codepoint;
while (next_it != range.end())
{
const ImWchar next_codepoint = *next_it;
if (next_codepoint != (end_codepoint + 1))
break;
// Yep, include it.
end_codepoint = next_codepoint;
++next_it;
}
ret.push_back(start_codepoint);
ret.push_back(end_codepoint);
it = next_it;
}
return ret;
}
void ImGuiManager::SetGlobalScale(float global_scale)
{
if (s_state.global_prescale == global_scale)
@ -247,7 +206,8 @@ bool ImGuiManager::Initialize(float global_scale, float screen_margin, Error* er
ImGuiIO& io = s_state.imgui_context->IO;
io.IniFilename = nullptr;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_RendererHasVtxOffset;
io.BackendFlags |=
ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures;
#ifndef __ANDROID__
// Android has no keyboard, nor are we using ImGui for any actual user-interactable windows.
io.ConfigFlags |=
@ -268,18 +228,9 @@ bool ImGuiManager::Initialize(float global_scale, float screen_margin, Error* er
SetStyle(s_state.imgui_context->Style, s_state.global_scale);
FullscreenUI::SetTheme();
if (!CompilePipelines(error))
if (!CreateFontAtlas(error) || !CompilePipelines(error))
return false;
if (!AddImGuiFonts(false, false) || !UpdateImGuiFontTexture())
{
Error::SetString(error, "Failed to create ImGui font text");
return false;
}
// don't need the font data anymore, save some memory
io.Fonts->ClearTexData();
NewFrame();
CreateSoftwareCursorTextures();
@ -290,17 +241,23 @@ void ImGuiManager::Shutdown()
{
DestroySoftwareCursorTextures();
s_state.debug_font = nullptr;
s_state.text_font = nullptr;
s_state.fixed_font = nullptr;
s_state.medium_font = nullptr;
s_state.large_font = nullptr;
ImGuiFullscreen::SetFonts(nullptr, nullptr);
ImGuiFullscreen::SetFont(nullptr);
s_state.imgui_pipeline.reset();
g_gpu_device->RecycleTexture(std::move(s_state.imgui_font_texture));
if (s_state.imgui_context)
{
for (ImTextureData* tex : s_state.imgui_context->IO.Fonts->TexList)
{
if (tex->Status == ImTextureStatus_Destroyed)
return;
std::unique_ptr<GPUTexture> gtex(reinterpret_cast<GPUTexture*>(tex->GetTexID()));
tex->Status = ImTextureStatus_Destroyed;
}
ImGui::DestroyContext(s_state.imgui_context);
s_state.imgui_context = nullptr;
}
@ -364,20 +321,19 @@ void ImGuiManager::UpdateScale()
const float window_scale =
(g_gpu_device && g_gpu_device->HasMainSwapChain()) ? g_gpu_device->GetMainSwapChain()->GetScale() : 1.0f;
const float scale = std::max(window_scale * s_state.global_prescale, 1.0f);
const bool scale_changed = (scale == s_state.global_scale);
if ((!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()) && scale == s_state.global_scale)
if (!ImGuiFullscreen::UpdateLayoutScale() && !scale_changed)
return;
s_state.global_scale = scale;
SetStyle(s_state.imgui_context->Style, s_state.global_scale);
if (!AddImGuiFonts(HasDebugFont(), HasFullscreenFonts()))
if (scale_changed)
{
GPUThread::ReportFatalErrorAndShutdown("Failed to create ImGui font text");
return;
s_state.global_scale = scale;
SetStyle(s_state.imgui_context->Style, s_state.global_scale);
}
UpdateImGuiFontTexture();
// force font GC
ImGui::GetIO().Fonts->CompactCache();
}
void ImGuiManager::NewFrame()
@ -469,30 +425,11 @@ bool ImGuiManager::CompilePipelines(Error* error)
return true;
}
bool ImGuiManager::UpdateImGuiFontTexture()
{
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
Error error;
const bool result = g_gpu_device->ResizeTexture(
&s_state.imgui_font_texture, static_cast<u32>(width), static_cast<u32>(height), GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, GPUTexture::Flags::None, pixels, sizeof(u32) * width, &error);
if (!result) [[unlikely]]
ERROR_LOG("Failed to resize ImGui font texture: {}", error.GetDescription());
// always update pointer, it could change
io.Fonts->SetTexID(s_state.imgui_font_texture.get());
return result;
}
void ImGuiManager::CreateDrawLists()
{
ImGui::EndFrame();
ImGui::Render();
UpdateTextures();
}
void ImGuiManager::RenderDrawLists(u32 window_width, u32 window_height, WindowInfo::PreRotation prerotation)
@ -547,7 +484,7 @@ void ImGuiManager::RenderDrawLists(u32 window_width, u32 window_height, WindowIn
clip = g_gpu_device->FlipToLowerLeft(clip, post_rotated_height);
g_gpu_device->SetScissor(clip);
g_gpu_device->SetTextureSampler(0, reinterpret_cast<GPUTexture*>(pcmd->TextureId),
g_gpu_device->SetTextureSampler(0, reinterpret_cast<GPUTexture*>(pcmd->GetTexID()),
g_gpu_device->GetLinearSampler());
if (pcmd->UserCallback) [[unlikely]]
@ -574,6 +511,73 @@ void ImGuiManager::RenderDrawLists(GPUTexture* texture)
RenderDrawLists(texture->GetWidth(), texture->GetHeight(), WindowInfo::PreRotation::Identity);
}
void ImGuiManager::UpdateTextures()
{
for (ImTextureData* const tex : s_state.imgui_context->IO.Fonts->TexList)
{
switch (tex->Status)
{
case ImTextureStatus_WantCreate:
{
DebugAssert(tex->Format == ImTextureFormat_RGBA32);
DEV_LOG("Create {}x{} ImGui texture", tex->Width, tex->Height);
Error error;
std::unique_ptr<GPUTexture> gtex = g_gpu_device->FetchTexture(
tex->Width, tex->Height, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8,
GPUTexture::Flags::None, tex->GetPixels(), tex->GetPitch(), &error);
if (!gtex) [[unlikely]]
{
ERROR_LOG("Failed to create {}x{} imgui texture: {}", tex->Width, tex->Height, error.GetDescription());
continue;
}
tex->SetTexID(reinterpret_cast<ImTextureID>(gtex.release()));
tex->Status = ImTextureStatus_OK;
}
break;
case ImTextureStatus_WantUpdates:
{
// TODO: Do we want to just update the whole dirty area? Probably need a heuristic...
GPUTexture* const gtex = reinterpret_cast<GPUTexture*>(tex->GetTexID());
for (const ImTextureRect& rc : tex->Updates)
{
DEV_LOG("Update {}x{} @ {},{} in {}x{} ImGui texture", rc.w, rc.h, rc.x, rc.y, tex->Width, tex->Height);
if (!gtex->Update(rc.x, rc.y, rc.w, rc.h, tex->GetPixelsAt(rc.x, rc.y), tex->GetPitch())) [[unlikely]]
{
ERROR_LOG("Failed to update {}x{} rect @ {},{} in imgui texture", rc.w, rc.h, rc.x, rc.y);
continue;
}
}
// Updates is cleared by ImGui NewFrame.
tex->Status = ImTextureStatus_OK;
}
break;
case ImTextureStatus_WantDestroy:
{
std::unique_ptr<GPUTexture> gtex(reinterpret_cast<GPUTexture*>(tex->GetTexID()));
if (gtex)
{
DEV_LOG("Destroy {}x{} ImGui texture", gtex->GetWidth(), gtex->GetHeight());
g_gpu_device->RecycleTexture(std::move(gtex));
}
tex->SetTexID(nullptr);
tex->Status = ImTextureStatus_Destroyed;
}
break;
case ImTextureStatus_Destroyed:
case ImTextureStatus_OK:
default:
continue;
}
}
}
void ImGuiManager::SetStyle(ImGuiStyle& style, float scale)
{
style = ImGuiStyle();
@ -763,20 +767,27 @@ void ImGuiManager::SetKeyMap()
bool ImGuiManager::LoadFontData(Error* error)
{
if (s_state.standard_font_data.empty())
Timer load_timer;
// only load used text fonts, that way we don't waste memory on mini
for (const TextFont text_font : s_state.text_font_order)
{
std::optional<DynamicHeapArray<u8>> font_data = s_state.font_path.empty() ?
Host::ReadResourceFile("fonts/Roboto-Regular.ttf", true, error) :
FileSystem::ReadBinaryFile(s_state.font_path.c_str(), error);
const u32 index = static_cast<u32>(text_font);
if (!s_state.text_fonts_data[index].empty())
continue;
std::optional<DynamicHeapArray<u8>> font_data =
Host::ReadResourceFile(TinyString::from_format("fonts/{}", TEXT_FONT_NAMES[index]), true, error);
if (!font_data.has_value())
return false;
s_state.standard_font_data = std::move(font_data.value());
s_state.text_fonts_data[index] = std::move(font_data.value());
}
if (s_state.fixed_font_data.empty())
{
std::optional<DynamicHeapArray<u8>> font_data = Host::ReadResourceFile("fonts/RobotoMono-Medium.ttf", true, error);
std::optional<DynamicHeapArray<u8>> font_data =
Host::ReadResourceFile("fonts/RobotoMono-VariableFont_wght.ttf", true, error);
if (!font_data.has_value())
return false;
@ -804,170 +815,120 @@ bool ImGuiManager::LoadFontData(Error* error)
if (s_state.emoji_font_data.empty())
{
std::optional<DynamicHeapArray<u8>> font_data =
Host::ReadCompressedResourceFile("fonts/TwitterColorEmoji-SVGinOT.ttf.zst", true, error);
Host::ReadResourceFile("fonts/TwitterColorEmoji-SVGinOT.ttf", true, error);
if (!font_data.has_value())
return false;
s_state.emoji_font_data = std::move(font_data.value());
}
DEV_LOG("Loading font data took {} ms", load_timer.GetTimeMilliseconds());
return true;
}
ImFont* ImGuiManager::AddTextFont(float size, const ImWchar* glyph_range)
bool ImGuiManager::CreateFontAtlas(Error* error)
{
ImFontConfig cfg;
cfg.FontDataOwnedByAtlas = false;
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_state.standard_font_data.data(), static_cast<int>(s_state.standard_font_data.size()), size, &cfg, glyph_range);
}
ImFont* ImGuiManager::AddFixedFont(float size)
{
ImFontConfig cfg;
cfg.FontDataOwnedByAtlas = false;
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.fixed_font_data.data(),
static_cast<int>(s_state.fixed_font_data.size()), size, &cfg,
ASCII_FONT_RANGE.data());
}
bool ImGuiManager::AddIconFonts(float size, const ImWchar* emoji_range)
{
{
ImFontConfig cfg;
cfg.MergeMode = true;
cfg.PixelSnapH = true;
cfg.GlyphMinAdvanceX = size;
cfg.GlyphMaxAdvanceX = size;
cfg.FontDataOwnedByAtlas = false;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.icon_fa_font_data.data(),
static_cast<int>(s_state.icon_fa_font_data.size()), size * 0.75f,
&cfg, FA_ICON_RANGE)) [[unlikely]]
{
return false;
}
}
{
ImFontConfig cfg;
cfg.MergeMode = true;
cfg.PixelSnapH = true;
cfg.GlyphMinAdvanceX = size;
cfg.GlyphMaxAdvanceX = size;
cfg.FontDataOwnedByAtlas = false;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.icon_pf_font_data.data(),
static_cast<int>(s_state.icon_pf_font_data.size()), size * 1.2f,
&cfg, PF_ICON_RANGE)) [[unlikely]]
{
return false;
}
}
{
ImFontConfig cfg;
cfg.MergeMode = true;
cfg.PixelSnapH = true;
cfg.GlyphMinAdvanceX = size;
cfg.GlyphMaxAdvanceX = size;
cfg.FontDataOwnedByAtlas = false;
cfg.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_LoadColor | ImGuiFreeTypeBuilderFlags_Bitmap;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.emoji_font_data.data(),
static_cast<int>(s_state.emoji_font_data.size()), size * 0.9f, &cfg,
emoji_range)) [[unlikely]]
{
return false;
}
}
return true;
}
bool ImGuiManager::AddImGuiFonts(bool debug_font, bool fullscreen_fonts)
{
const float window_scale =
(g_gpu_device && g_gpu_device->HasMainSwapChain()) ? g_gpu_device->GetMainSwapChain()->GetScale() : 1.0f;
const float debug_font_size = std::ceil(15.0f * window_scale);
const float standard_font_size = std::ceil(15.0f * s_state.global_scale);
const float osd_font_size = std::ceil(17.0f * s_state.global_scale);
INFO_LOG("Allocating fonts winscale={} globalscale={} debug={} fullscreen={}", window_scale, s_state.global_scale,
debug_font, fullscreen_fonts);
// need to generate arrays if dynamic ranges are present
const ImWchar* text_range = s_state.font_range.empty() ? DEFAULT_FONT_RANGE.data() : s_state.font_range.data();
const ImWchar* emoji_range = EMOJI_ICON_RANGE;
std::vector<ImWchar> full_text_range, full_emoji_range;
if (!s_state.dynamic_font_range.empty())
{
// skip the zeros, we'll add them afterwards
const size_t base_size = s_state.font_range.empty() ? DEFAULT_FONT_RANGE.size() : s_state.font_range.size();
Assert(base_size > 2);
full_text_range.reserve(base_size + s_state.dynamic_font_range.size());
full_text_range.insert(full_text_range.end(), &text_range[0], &text_range[base_size - 2]);
full_text_range.insert(full_text_range.end(), s_state.dynamic_font_range.begin(), s_state.dynamic_font_range.end());
full_text_range.insert(full_text_range.end(), 2, 0);
text_range = full_text_range.data();
}
if (!s_state.dynamic_emoji_range.empty())
{
// skip the zeros, we'll add them afterwards
size_t base_size = 0;
for (const ImWchar* c = EMOJI_ICON_RANGE; *c != 0; c++)
base_size++;
Assert(base_size > 2);
full_emoji_range.reserve(base_size + s_state.dynamic_emoji_range.size());
full_emoji_range.insert(full_emoji_range.end(), &EMOJI_ICON_RANGE[0], &EMOJI_ICON_RANGE[base_size]);
full_emoji_range.insert(full_emoji_range.end(), s_state.dynamic_emoji_range.begin(),
s_state.dynamic_emoji_range.end());
full_emoji_range.insert(full_emoji_range.end(), 2, 0);
emoji_range = full_emoji_range.data();
}
Timer load_timer;
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
if (debug_font)
const float default_text_size = GetOSDFontSize();
const float default_text_weight = 400.0f;
const float default_fixed_weight = 500.0f;
ImFontConfig text_cfg;
text_cfg.FontDataOwnedByAtlas = false;
// First text font has to be added before the icon fonts.
// Remaining fonts are added after the icon font, otherwise the wrong glyphs will be used in the UI.
const TextFont first_font = s_state.text_font_order.front();
auto& first_font_data = s_state.text_fonts_data[static_cast<size_t>(first_font)];
Assert(!first_font_data.empty());
s_state.text_font =
ImGui::GetIO().Fonts->AddFontFromMemoryTTF(first_font_data.data(), static_cast<int>(first_font_data.size()),
default_text_size, default_text_weight, &text_cfg);
if (!s_state.text_font)
{
s_state.debug_font = AddTextFont(debug_font_size, ASCII_FONT_RANGE.data());
if (!s_state.debug_font)
return false;
Error::SetStringFmt(error, "Failed to add primary text font {}", static_cast<u32>(s_state.text_font_order.front()));
return false;
}
s_state.fixed_font = AddFixedFont(standard_font_size);
// Add icon fonts.
ImFontConfig icon_cfg;
icon_cfg.MergeMode = true;
icon_cfg.FontDataOwnedByAtlas = false;
icon_cfg.PixelSnapH = true;
icon_cfg.GlyphMinAdvanceX = default_text_size;
icon_cfg.GlyphMaxAdvanceX = default_text_size;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.icon_fa_font_data.data(),
static_cast<int>(s_state.icon_fa_font_data.size()),
default_text_size * 0.75f, 0.0f, &icon_cfg)) [[unlikely]]
{
Error::SetStringView(error, "Failed to add FA icon font");
return false;
}
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.icon_pf_font_data.data(),
static_cast<int>(s_state.icon_pf_font_data.size()),
default_text_size * 1.2f, 0.0f, &icon_cfg)) [[unlikely]]
{
Error::SetStringView(error, "Failed to add PF icon font");
return false;
}
// Only for emoji font.
icon_cfg.FontLoaderFlags = ImGuiFreeTypeLoaderFlags_LoadColor | ImGuiFreeTypeLoaderFlags_Bitmap;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.emoji_font_data.data(),
static_cast<int>(s_state.emoji_font_data.size()),
default_text_size * 0.9f, 0.0f, &icon_cfg)) [[unlikely]]
{
Error::SetStringView(error, "Failed to add emoji icon font");
return false;
}
// Now we can add the remaining text fonts.
text_cfg.MergeMode = true;
for (size_t i = 1; i < s_state.text_font_order.size(); i++)
{
const TextFont text_font_idx = s_state.text_font_order[i];
if (text_font_idx == first_font)
continue;
auto& font_data = s_state.text_fonts_data[static_cast<size_t>(text_font_idx)];
Assert(!font_data.empty());
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(font_data.data(), static_cast<int>(font_data.size()),
default_text_size, default_text_weight, &text_cfg))
{
Error::SetStringFmt(error, "Failed to add text font {}", static_cast<u32>(text_font_idx));
return false;
}
}
// Add the fixed-width font separately last.
ImFontConfig fixed_cfg;
fixed_cfg.FontDataOwnedByAtlas = false;
s_state.fixed_font = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.fixed_font_data.data(),
static_cast<int>(s_state.fixed_font_data.size()),
GetFixedFontSize(), default_fixed_weight, &fixed_cfg);
if (!s_state.fixed_font)
return false;
s_state.osd_font = AddTextFont(osd_font_size, text_range);
if (!s_state.osd_font || !AddIconFonts(osd_font_size, emoji_range))
return false;
if (!debug_font)
s_state.debug_font = s_state.osd_font;
if (fullscreen_fonts)
{
const float medium_font_size = ImGuiFullscreen::LayoutScale(ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE);
s_state.medium_font = AddTextFont(medium_font_size, text_range);
if (!s_state.medium_font || !AddIconFonts(medium_font_size, emoji_range))
return false;
const float large_font_size = ImGuiFullscreen::LayoutScale(ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE);
s_state.large_font = AddTextFont(large_font_size, text_range);
if (!s_state.large_font || !AddIconFonts(large_font_size, emoji_range))
return false;
}
else
{
s_state.medium_font = nullptr;
s_state.large_font = nullptr;
Error::SetStringView(error, "Failed to add fixed-width font");
return false;
}
ImGuiFullscreen::SetFonts(s_state.medium_font, s_state.large_font);
ImGuiFullscreen::SetFont(s_state.text_font);
return io.Fonts->Build();
if (!io.Fonts->Build())
{
Error::SetStringView(error, "Build() failed");
return false;
}
DEV_LOG("Creating font atlas took {} ms", load_timer.GetTimeMilliseconds());
return true;
}
void ImGuiManager::ReloadFontDataIfActive()
@ -977,74 +938,16 @@ void ImGuiManager::ReloadFontDataIfActive()
ImGui::EndFrame();
if (!LoadFontData(nullptr))
Error error;
if (!CreateFontAtlas(&error)) [[unlikely]]
{
GPUThread::ReportFatalErrorAndShutdown("Failed to load font data");
GPUThread::ReportFatalErrorAndShutdown(fmt::format("Failed to recreate font atlas:\n{}", error.GetDescription()));
return;
}
if (!AddImGuiFonts(HasDebugFont(), HasFullscreenFonts()))
{
GPUThread::ReportFatalErrorAndShutdown("Failed to create ImGui font text");
return;
}
UpdateImGuiFontTexture();
NewFrame();
}
bool ImGuiManager::AddFullscreenFontsIfMissing()
{
if (HasFullscreenFonts())
return true;
// can't do this in the middle of a frame
ImGui::EndFrame();
const bool debug_font = HasDebugFont();
if (!AddImGuiFonts(debug_font, true))
{
GPUThread::ReportFatalErrorAndShutdown("Failed to lazily allocate fullscreen fonts.");
AddImGuiFonts(debug_font, false);
}
UpdateImGuiFontTexture();
NewFrame();
return HasFullscreenFonts();
}
bool ImGuiManager::HasDebugFont()
{
return (s_state.debug_font != s_state.osd_font);
}
bool ImGuiManager::AddDebugFontIfMissing()
{
if (HasDebugFont())
return true;
// can't do this in the middle of a frame
ImGui::EndFrame();
const bool fullscreen_font = HasFullscreenFonts();
if (!AddImGuiFonts(true, fullscreen_font))
{
ERROR_LOG("Failed to lazily allocate fullscreen fonts.");
AddImGuiFonts(true, fullscreen_font);
}
UpdateImGuiFontTexture();
NewFrame();
return HasDebugFont();
}
bool ImGuiManager::HasFullscreenFonts()
{
return (s_state.medium_font && s_state.large_font);
}
void ImGuiManager::AddOSDMessage(std::string key, std::string message, float duration, bool is_warning)
{
if (!key.empty())
@ -1162,7 +1065,9 @@ void ImGuiManager::DrawOSDMessages(Timer::Value current_time)
static constexpr float MOVE_DURATION = 0.5f;
ImFont* const font = s_state.osd_font;
ImFont* const font = s_state.text_font;
const float font_size = GetOSDFontSize();
const float font_weight = UIStyle.NormalFontWeight;
const float scale = s_state.global_scale;
const float spacing = std::ceil(6.0f * scale);
const float margin = std::ceil(s_state.screen_margin * scale);
@ -1186,7 +1091,8 @@ void ImGuiManager::DrawOSDMessages(Timer::Value current_time)
++iter;
const ImVec2 text_size = font->CalcTextSizeA(font->FontSize, max_width, max_width, IMSTR_START_END(msg.text));
const ImVec2 text_size =
font->CalcTextSizeA(font_size, font_weight, max_width, max_width, IMSTR_START_END(msg.text));
float box_width = text_size.x + padding + padding;
const float box_height = text_size.y + padding + padding;
@ -1265,7 +1171,7 @@ void ImGuiManager::DrawOSDMessages(Timer::Value current_time)
dl->AddRectFilled(pos, pos_max, ImGui::GetColorU32(ModAlpha(UIStyle.ToastBackgroundColor, opacity * 0.95f)),
rounding);
RenderShadowedTextClipped(dl, font, text_rect.Min, text_rect.Max,
RenderShadowedTextClipped(dl, font, font_size, font_weight, text_rect.Min, text_rect.Max,
ImGui::GetColorU32(ModAlpha(UIStyle.ToastTextColor, opacity)), msg.text, &text_size,
ImVec2(0.0f, 0.0f), max_width, &text_rect, scale);
@ -1333,14 +1239,14 @@ float ImGuiManager::GetScreenMargin()
return s_state.screen_margin;
}
ImFont* ImGuiManager::GetDebugFont()
ImFont* ImGuiManager::GetTextFont()
{
return s_state.debug_font;
return s_state.text_font;
}
ImFont* ImGuiManager::GetOSDFont()
float ImGuiManager::GetFixedFontSize()
{
return s_state.osd_font;
return std::ceil(15.0f * s_state.global_scale);
}
ImFont* ImGuiManager::GetFixedFont()
@ -1348,16 +1254,14 @@ ImFont* ImGuiManager::GetFixedFont()
return s_state.fixed_font;
}
ImFont* ImGuiManager::GetMediumFont()
float ImGuiManager::GetDebugFontSize(float window_scale)
{
AddFullscreenFontsIfMissing();
return s_state.medium_font;
return std::ceil(15.0f * window_scale);
}
ImFont* ImGuiManager::GetLargeFont()
float ImGuiManager::GetOSDFontSize()
{
AddFullscreenFontsIfMissing();
return s_state.large_font;
return std::ceil(17.0f * s_state.global_scale);
}
bool ImGuiManager::WantsTextInput()
@ -1739,13 +1643,12 @@ bool ImGuiManager::CreateAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state
return false;
}
AddDebugFontIfMissing();
state->imgui_context = ImGui::CreateContext(s_state.imgui_context->IO.Fonts);
state->imgui_context->Viewports[0]->Size = state->imgui_context->IO.DisplaySize =
ImVec2(static_cast<float>(state->swap_chain->GetWidth()), static_cast<float>(state->swap_chain->GetHeight()));
state->imgui_context->IO.IniFilename = nullptr;
state->imgui_context->IO.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
state->imgui_context->IO.BackendFlags |=
ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures;
state->imgui_context->IO.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
SetCommonIOOptions(state->imgui_context->IO);
@ -1809,15 +1712,17 @@ bool ImGuiManager::RenderAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state
ImGui::SetCurrentContext(state->imgui_context);
const float window_scale = state->swap_chain->GetScale();
ImGui::NewFrame();
ImGui::PushFont(s_state.debug_font);
ImGui::PushFont(s_state.text_font, GetDebugFontSize(window_scale));
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Always);
ImGui::SetNextWindowSize(state->imgui_context->IO.DisplaySize, ImGuiCond_Always);
if (ImGui::Begin("AuxRenderWindowMain", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse))
{
draw_callback(state->swap_chain->GetScale());
draw_callback(window_scale);
}
ImGui::End();

View File

@ -7,6 +7,7 @@
#include "common/types.h"
#include <array>
#include <memory>
#include <span>
#include <string>
@ -57,6 +58,17 @@ namespace ImGuiManager {
using WCharType = u32;
enum class TextFont : u8
{
Default,
Chinese,
Japanese,
Korean,
MaxCount
};
using TextFontOrder = std::array<TextFont, static_cast<size_t>(TextFont::MaxCount)>;
/// Default size for screen margins.
#ifndef __ANDROID__
static constexpr float DEFAULT_SCREEN_MARGIN = 10.0f;
@ -64,15 +76,9 @@ static constexpr float DEFAULT_SCREEN_MARGIN = 10.0f;
static constexpr float DEFAULT_SCREEN_MARGIN = 16.0f;
#endif
/// Sets the path to the font to use. Empty string means to use the default.
void SetFontPathAndRange(std::string path, std::vector<WCharType> range);
/// Sets the normal/emoji font range to use. Empty means no glyphs will be rasterized.
/// Should NOT be terminated with zeros, unlike the font range above.
void SetDynamicFontRange(std::vector<WCharType> font_range, std::vector<WCharType> emoji_range);
/// Returns a compacted font range, with adjacent glyphs merged into one pair.
std::vector<WCharType> CompactFontRange(std::span<const WCharType> range);
/// Sets the order for text fonts.
TextFontOrder GetDefaultTextFontOrder();
void SetTextFontOrder(const TextFontOrder& order);
/// Changes the global scale.
void SetGlobalScale(float global_scale);
@ -121,34 +127,18 @@ float GetGlobalScale();
/// Returns the screen margins, or "safe zone".
float GetScreenMargin();
/// Returns true if fullscreen fonts are present.
bool HasFullscreenFonts();
/// Allocates/adds fullscreen fonts if they're not loaded.
bool AddFullscreenFontsIfMissing();
/// Returns true if there is a separate debug font.
bool HasDebugFont();
/// Changes whether a debug font is generated. Otherwise, the OSD font will be used for GetStandardFont().
bool AddDebugFontIfMissing();
/// Returns the standard font for external drawing.
ImFont* GetDebugFont();
/// Returns the standard font for on-screen display drawing.
ImFont* GetOSDFont();
ImFont* GetTextFont();
/// Returns the fixed-width font for external drawing.
float GetFixedFontSize();
ImFont* GetFixedFont();
/// Returns the medium font for external drawing, scaled by ImGuiFullscreen.
/// This font is allocated on demand.
ImFont* GetMediumFont();
/// Returns the standard font for external drawing.
float GetDebugFontSize(float window_scale);
/// Returns the large font for external drawing, scaled by ImGuiFullscreen.
/// This font is allocated on demand.
ImFont* GetLargeFont();
/// Returns the font size for rendering the OSD.
float GetOSDFontSize();
/// Returns true if imgui wants to intercept text input.
bool WantsTextInput();