-
-
Notifications
You must be signed in to change notification settings - Fork 10.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dear ImGui tips & trick café! #7081
Comments
I'll post something to get the ball rolling.. Appending to a windowI believe this is widely known? But it is possible to use call Begin(), BeginChild(), BeginMenu(), BeginTabBar(), BeginTooltp() with the same identifier to append to the same window. // called first
void Function1()
{
ImGui::Begin("Some window");
ImGui::Text("Hello world");
ImGui::End();
}
// called later
void Function2()
{
ImGui::Begin("Some window");
ImGui::Checkbox("Checkbox", &some_value);
ImGui::End();
} One nice property of that is that you can easily trace an algorithm by calling code from leaf functions: #define IM_TRACE_LOCATION() if (ImGui::Begin("Function Trace")) { ImGui::Text("%s(), %s::%d", __FUNCTION__, __FILE__, __LINE__);} ImGui::End();
void Function1()
{
IM_TRACE_LOCATION();
//...
}
void Function2()
{
IM_TRACE_LOCATION();
//...
}
void MainLoop()
{
Function1();
Function2();
Function1();
} |
Your turn ! :) |
A rather simple trick, but useful nonetheless: if ( ImGui::Begin( "MultiCheckbox" ) )
{
static bool first = false;
static bool second = false;
static bool third = false;
ImGui::Checkbox( "First", &first );
ImGui::Checkbox( "Second", &second );
ImGui::Checkbox( "Third", &third );
ImGui::Separator();
bool* all[] = { &first, &second, &third };
size_t count = sizeof( all ) / sizeof( all[ 0 ] );
size_t numSet = 0;
for ( size_t i = 0; i < count; ++i )
if ( *all[ i ] )
numSet++;
int flags = (int)( numSet == count ) * 2 + (int)( numSet > 0 );
if ( ImGui::CheckboxFlags( "All", &flags, 3 ) )
{
for ( size_t i = 0; i < count; ++i )
*all[ i ] = flags != 0;
}
}
ImGui::End(); |
I don't know if that counts, but here is a simple button with shadow effect: bool ButtonWithShadow(const char*label, ImVec2 buttonSize, ImVec4 color)
{
ImGui::PushStyleColor(ImGuiCol_Button, color);
bool pressed = ImGui::Button(label, buttonSize);
// Draw a gradient on top of the button
{
ImVec2 tl = ImGui::GetItemRectMin();
ImVec2 br = ImGui::GetItemRectMax();
ImVec2 size = ImGui::GetItemRectSize();
float k = 0.3f;
ImVec2 tl_middle(tl.x, tl.y + size.y * (1.f - k));
ImVec2 br_middle(br.x, tl.y + size.y * k);
ImVec4 col_darker(0.f, 0.f, 0.f, 0.2f);
ImVec4 col_interm(0.f, 0.f, 0.f, 0.1f);
ImVec4 col_transp(0.f, 0.f, 0.f, 0.f);
ImGui::GetForegroundDrawList()->AddRectFilledMultiColor(
tl,
br_middle,
ImGui::GetColorU32(col_interm), // upper left
ImGui::GetColorU32(col_interm), // upper right
ImGui::GetColorU32(col_transp), // bottom right
ImGui::GetColorU32(col_transp) // bottom left
);
ImGui::GetForegroundDrawList()->AddRectFilledMultiColor(
tl_middle,
br,
ImGui::GetColorU32(col_transp), // upper left
ImGui::GetColorU32(col_transp), // upper right
ImGui::GetColorU32(col_darker), // bottom right
ImGui::GetColorU32(col_darker) // bottom left
);
}
ImGui::PopStyleColor();
return pressed;
} ![]() In use here |
Opening specific main menus with keyboard shortcuts using OpenPopup. I believe this is undocumented, unless you know menus are just popups. Also some other menu shortcut ideas. There might be some better api baking for this, but it's pretty simple already... Edit: Make MenuItem get the shortcut name automatically namespace ImGui {
// Wrapper for menu that can be opened with a global shortcut
// or submenu with a local shortcut
inline bool BeginMenu(const char* label, const ImGuiKeyChord key)
{
if (ImGui::IsKeyChordPressed(key))
ImGui::OpenPopup(label);
return ImGui::BeginMenu(label);
};
// Wrapper for menuitem that can be opened with a local shortcut
inline bool MenuItem(const char* label, const ImGuiKeyChord key)
{
char shortcut[32];
ImGui::GetKeyChordName(key, shortcut, IM_ARRAYSIZE(shortcut));
return ImGui::MenuItem(label, shortcut) || ImGui::IsKeyChordPressed(key);
}
}
if (ImGui::BeginMainMenuBar()) {
// Alt+F opens the File menu by popup id and allows
// further keyboard navigation with arrows too
if (ImGui::BeginMenu("File", ImGuiMod_Alt | ImGuiKey_F)) {
// This global shortcut is handled later
if (ImGui::MenuItem("Global Exit", "Alt+X"))
done = true;
// Local shortcuts can be handled inline
if (ImGui::MenuItem("Local Exit", ImGuiMod_Alt | ImGuiKey_Z))
done = true;
// The S shortcut works locally, but there is no code to draw shortcuts for submenus
// The shortcut code from MenuItemEx could be used in the wrapper to draw it though
if (ImGui::BeginMenu("Submenu", ImGuiKey_S)) {
if (ImGui::MenuItem("Also Exit", ImGuiKey_A))
done = true;
ImGui::EndMenu();
}
ImGui::EndMenu();
}
// Alt+E opens the Edit menu
if (ImGui::BeginMenu("Edit", ImGuiMod_Alt | ImGuiKey_E)) {
if (ImGui::MenuItem("Nope")) {}
ImGui::EndMenu();
}
// Handle that global shortcut too
if (ImGui::IsKeyChordPressed(ImGuiMod_Alt | ImGuiKey_X))
done = true;
ImGui::EndMainMenuBar();
} |
@pthom Great! Also linking to #4722 for similar ideas.
@Dregu Great! I didn't even know it was possible, as I'd have been mislead by the fact that menus re-use custom window names, but instead of the popup id system is standard. FYI i will be working on ways to support that (#456) but there has been lots of yak-shaving on the way forward. You may remove the "const char* shortcut, " parameter and use |
A simple one: implement Instead of: ImGui::SetCursorPos(ImVec2(145.f, 290.f));
ImGui::Button("whatever", ImVec2(14.5f, 29.f)); use: ImGui::SetCursorPos(EmVec2(10.f, 20.f));
ImGui::Button("whatever", EmVec2(1.f, 2.f)); Note: EmToVec2() returns a size in multiples of the font height. It is somewhat comparable to the em CSS Unit. By default, the font height on ImGui is 14.5, hence the new values. Alternatively, you can use ImGui::SetCursorPos(ImVec2(145.f * kDpi(), 290.f * kDpi()));
ImGui::Button("whatever", ImVec2(14.5f * kDpi(), 29.f * kDpi())); And here are the definitions of ImVec2 EmVec2(float x, float y) { float k = ImGui::GetFontSize(); return ImVec2(k * x, k * y); }
float kDpi() { return ImGui::GetFontSize() / 14.5f; }
float EmSize(float nbLines = 1.f) { return ImGui::GetFontSize() * nbLines; } |
A lightweight RAII helper class ( Requires C++17 or higher ) template <auto fn, auto... args>
class scope_exit
{
public:
constexpr scope_exit() = default;
constexpr scope_exit(bool turn_on) : turn_on(turn_on) { }
constexpr scope_exit(const scope_exit&) = delete;
constexpr scope_exit& operator=(const scope_exit&) = delete;
constexpr scope_exit(scope_exit&& rhs) noexcept : turn_on(rhs.turn_on) { rhs.turn_on = false; }
constexpr scope_exit& operator=(scope_exit&& rhs) noexcept
{
bool temp = rhs.turn_on;
rhs.turn_on = false;
turn_on = temp;
return *this;
}
constexpr ~scope_exit()
{
if (turn_on)
fn(args...);
}
constexpr operator bool() const noexcept
{
return turn_on;
}
bool turn_on = true;
}; RAII wrapper function for imgui template <auto fn>
struct window_guard
{
scope_exit<fn> guard;
bool not_skip;
operator bool() const noexcept
{
return not_skip;
}
};
[[nodiscard]] inline window_guard<ImGui::End>
make_scoped_window(const char* name, bool* p_open = nullptr, ImGuiWindowFlags flags = 0)
{
return { .not_skip = ImGui::Begin(name, p_open, flags) };
}
[[nodiscard]] inline scope_exit<ImGui::EndMenuBar> make_scoped_menu_bar()
{
return { ImGui::BeginMenuBar() };
}
[[nodiscard]] inline scope_exit<ImGui::PopStyleColor, 1>
make_scoped_style_color(ImGuiCol idx, ImU32 col)
{
ImGui::PushStyleColor(idx, col);
return {};
} example (a simple window): before if (ImGui::Begin("window", nullptr, ImGuiWindowFlags_MenuBar))
{
if (ImGui::BeginMenuBar())
{
ImGui::Text("this is a menu bar!");
ImGui::EndMenuBar();
}
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_Button));
ImGui::Text("this is colored text!");
ImGui::PopStyleColor();
}
ImGui::End(); after if (auto window = make_scoped_window("window", nullptr, ImGuiWindowFlags_MenuBar))
{
if (auto menu = make_scoped_menu_bar())
ImGui::Text("this is a menu bar!");
auto color = make_scoped_style_color(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_Button));
ImGui::Text("this is colored text!");
} |
Not sure where to draw the line but I think it'd be good if the thread was focused on standalone, narrow-scope, readily usable tips, rather than ones which already have whole issues/topics/wiki sections open and may be wider topics. For DPI also see "dpi" labels. There are many alternative ways to handle DPI, I suppose our problem is not settling on a common idiom satisfying all cases (it's a bit tentacular due to the connection with multi-viewports coordinates). For RAII also see #2197 #2096 and https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness |
Agreed. I simplified the Dpi related tip to the simple |
I have a trick for windows imgui applications. The default prerender screen is white so you get flash banged when opening dark mode type applications. You can set a variable in WNDCLASSEX to prevent this from happening: default_background_color.mp4dark_background_color.mp4 |
Quick and dirty function for right alignment. bool ImRightAlign(const char* str_id)
{
if(ImGui::BeginTable(str_id, 2, ImGuiTableFlags_SizingFixedFit, ImVec2(-1,0)))
{
ImGui::TableSetupColumn("a", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
return true;
}
return false;
}
#define ImEndRightAlign ImGui::EndTable Usage ImGui::Text("Some left aligned text");
ImGui::SameLine();
if (ImRightAlign("btns"))
{
if (ImGui::Button("ButtonA")) btnA();
ImGui::SameLine();
if (ImGui::Button("ButtonB")) btnB();
ImGui::SameLine();
if (ImGui::Button("ButtonC")) btnC();
ImEndRightAlign();
} |
Wrapping dear imgui's runtime type dispatch in several widgets with compile-time dispatch for simpler usage, type safety between arguments, and an API cleanup (taking min/max by value): namespace ImGui {
template<typename> constexpr ImGuiDataType DataTypeEnum = ImGuiDataType_COUNT;
template<> constexpr inline ImGuiDataType DataTypeEnum<int8_t> = ImGuiDataType_S8;
template<> constexpr inline ImGuiDataType DataTypeEnum<uint8_t> = ImGuiDataType_U8;
template<> constexpr inline ImGuiDataType DataTypeEnum<int16_t> = ImGuiDataType_S16;
template<> constexpr inline ImGuiDataType DataTypeEnum<uint16_t> = ImGuiDataType_U16;
template<> constexpr inline ImGuiDataType DataTypeEnum<int32_t> = ImGuiDataType_S32;
template<> constexpr inline ImGuiDataType DataTypeEnum<uint32_t> = ImGuiDataType_U32;
template<> constexpr inline ImGuiDataType DataTypeEnum<int64_t> = ImGuiDataType_S64;
template<> constexpr inline ImGuiDataType DataTypeEnum<uint64_t> = ImGuiDataType_U64;
template<> constexpr inline ImGuiDataType DataTypeEnum<float> = ImGuiDataType_Float;
template<> constexpr inline ImGuiDataType DataTypeEnum<double> = ImGuiDataType_Double;
template<typename T> concept ScalarType = (DataTypeEnum<T> != ImGuiDataType_COUNT);
template<ScalarType T>
bool Drag(const char* l, T* p, float speed, T min = std::numeric_limits<T>::lowest(), T max = std::numeric_limits<T>::max(), const char* fmt = nullptr, ImGuiSliderFlags flags = 0) {
return DragScalar(l, DataTypeEnum<T>, p, speed, &min, &max, fmt, flags);
}
template<ScalarType T>
bool DragN(const char* l, T* p, int n, float speed, T min = std::numeric_limits<T>::lowest(), T max = std::numeric_limits<T>::max(), const char* fmt = nullptr, ImGuiSliderFlags flags = 0) {
return DragScalarN(l, DataTypeEnum<T>, p, n, speed, &min, &max, fmt, flags);
}
template<ScalarType T>
bool Slider(const char* l, T* p, T min = std::numeric_limits<T>::lowest(), T max = std::numeric_limits<T>::max(), const char* fmt = nullptr, ImGuiSliderFlags flags = 0) {
return SliderScalar(l, DataTypeEnum<T>, p, &min, &max, fmt, flags);
}
template<ScalarType T>
bool SliderN(const char* l, T* p, int n, T min = std::numeric_limits<T>::lowest(), T max = std::numeric_limits<T>::max(), const char* fmt = nullptr, ImGuiSliderFlags flags = 0) {
return SliderScalar(l, DataTypeEnum<T>, p, n, &min, &max, fmt, flags);
}
template<ScalarType T>
bool InputNumber(const char* l, T* p, T step = 0, T step_fast = 0, const char* fmt = nullptr, ImGuiInputTextFlags flags = 0) {
return InputScalar(l, DataTypeEnum<T>, p, (step == 0 ? nullptr : &step), (step_fast == 0 ? nullptr : &step_fast), fmt, flags);
}
template<ScalarType T>
bool InputNumberN(const char* l, T* p, int n, T step = 0, T step_fast = 0, const char* fmt = nullptr, ImGuiInputTextFlags flags = 0) {
return InputScalarN(l, DataTypeEnum<T>, p, n, (step == 0 ? nullptr : &step), (step_fast == 0 ? nullptr : &step_fast), fmt, flags);
}
} // namespace ImGui Trivial further improvements:
example usage: static int i;
static double d;
ImGui::Slider("i", &i);
ImGui::Slider("d", &d); |
Care to share how i can do this? |
My simple solution: WNDCLASSEXW wc =
{sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr,
::CreateSolidBrush(RGB(239.6, 239.6, 239.6)), nullptr,
window_name_, nullptr}; |
Hi, which source file would i apply this to and where in the file? Im a begginer but i have managed to build imgui successfuly. Thanks. |
That is part of your own code, so in whichever source file you are registering window classes. You might have copied that part from the ImGui example applications, in those examples it is the main.cpp file. |
Thanks, it's actually pyGUI that im using so i'll try to figure it out. |
A quick hacky function I made that spans tab buttons across a specified width: static size_t tabIndex = 1;
void SpanTabAcrossWidth(const float width, const size_t tabs = 1) {
if (width <= 0.0f)
return;
const float oneTabWidthWithSpacing = width / tabs;
const float oneTabWidth = oneTabWidthWithSpacing - (tabIndex == tabs ? 0.0f : GImGui->Style.ItemSpacing.x / 2.0f);
ImGui::SetNextItemWidth(oneTabWidth);
tabIndex++;
}
void EndTabBar() {
ImGui::EndTabBar();
tabIndex = 1;
} Example: static constexpr ImGuiChildFlags childFlags = ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX;
ImGui::Begin("Test Window", nullptr, ImGuiWindowFlags_AlwaysAutoResize); {
if (ImGui::BeginTabBar("##MainTabBar")) {
static float childWidth = 0.0f;
SpanTabAcrossWidth(childWidth, 2);
if (ImGui::BeginTabItem("FirstTab")) {
ImGui::BeginChild("##TabChild", ImVec2(0.0f, 25.0f), childFlags);
{
childWidth = ImGui::GetItemRectSize().x;
ImGui::Text("This child is kinda short!");
ImGui::EndChild();
}
ImGui::EndTabItem();
}
SpanTabAcrossWidth(childWidth, 2);
if (ImGui::BeginTabItem("SecondTab")) {
ImGui::BeginChild("##TabChild", ImVec2(0.0f, 25.0f), childFlags);
{
childWidth = ImGui::GetItemRectSize().x;
ImGui::Text("This child is way longerrrrrrrrrrrrrrrr!");
ImGui::EndChild();
}
ImGui::EndTabItem();
}
EndTabBar();
}
ImGui::End();
} |
the ability to have mac os alike global menu bar static bool menuOpened = true;
ImGui::Begin("database", 0, ImGuiWindowFlags_NoScrollWithMouse);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) || menuOpened ) {
menuOpened = false;
ImGui::BeginMainMenuBar();
if ( ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) )
menuOpened = true;
if (ImGui::BeginMenu("File")) {
ImGui::EndMenu();
menuOpened = true;
}
ImGui::EndMainMenuBar();
} |
A slider that can edit positive floats in any range, with a given number of significant digits. For any value, the slider will interactively adapt its range to the value, and briefly flash red when the range changed. The code is a bit too long to fit in here, so it is available in a gist Below in action with 5 significant digits, and a current range of 0-100: it is about to change range if the user drags to the right... And after the user continued to drag right, there is a brief red flash, and a new interval can be used |
Make any widget resizable (InputTextMultiLine, ImPlot, etc.). Example of a resizable plot https://gist.github.com/pthom/0a532a1d487ded4744f113a7b100511c (Note: this gist might be out of date. The actual implementation is here) |
I created a set of helpers to integrate OpenGL views into the UI in a convenient manner, and perhaps more importantly, allow them to be rendered independently from UI updates. This is especially useful for desktop applications when you only want to render when an event occurs to save GPU resources (else it hammers any iGPU on a high refresh-rate screen to death). With this, you can have single text labels and OpenGL views be updated at your desired refresh rate (e.g. capped to 60Hz) but only call ImGui::NewFrame and render the whole screen whenever an event occurs. If you just want the functionality and use the OpenGL3 backend, you can use it pretty easily. https://gist.github.com/Seneral/b4b34a283539938869cd10b2d065a88c In the following image, the marked areas are OnDemandItems and are rendered at 60Hz without even calling ImGui::NewFrame, the rest of the UI is only updated and rendered on events (e.g. mouse move). Code usage looks like this for the GL views: AddOnDemandRender(ImGui::GetCurrentWindowRead()->InnerRect, [](const ImDrawList* dl, const ImDrawCmd* dc)
{
OnDemandItem &render = *static_cast<OnDemandItem*>(dc->UserCallbackData);
ImVec2 size = SetOnDemandRenderArea(render, dc->ClipRect);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
// Update and render visualisations
CameraView &view = *(CameraView*)render.userData; // Make sure this still exists when rendering!
visualiseCamera(view, size);
}, &view); And like this for the label: AddOnDemandText("Frame 00000000", [](const ImDrawList* dl, const ImDrawCmd* dc)
{
RenderOnDemandText(*static_cast<OnDemandItem*>(dc->UserCallbackData), "Frame %d", GetState().frameNum.load());
}); |
How to play imgui in wsl
Wsl Command List### Remove Previous Installed Ubuntu
wsl --unregister Ubuntu-22.04
### Look for available subsystem to install
wsl --list --online
### Install Ubuntu22.04
wsl --install -d Ubuntu-22.04
### Update wsl
wsl --update
### Shutdown wsl
wsl --shutdown Source Change(For CN Users)
sudo nano /etc/apt/sources.list
deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse Install Development Enviroment### Some tools
sudo apt install gedit -y
sudo apt install nautilus -y
### Install the Desktop & apps
#### Update ubuntu
sudo apt update && sudo apt upgrade
#### Install by tasksel
sudo apt install tasksel
sudo tasksel install ubuntu-desktop
#### Command Before Install to prevent error
sudo apt-mark hold acpid acpi-support
#### The core Command Takes lots of time
sudo apt install ubuntu-desktop
#### Command After Install to repair error
sudo rm -rf /etc/acpi/events
### Install Compilor & Graphic api
sudo apt install mesa-utils
sudo apt install build-essential
sudo apt install libglfw3 libglfw3-dev
Complie Project From Github(ImGui)### Branch git Command
git clone -b docking --single-branch https://github.com/ocornut/imgui/
### Master Branch If You Want
### git clone https://github.com/ocornut/imgui
### Make Command
cd imgui/examples/example_glfw_opengl3
make
### Run!
./example_glfw_opengl3
Some tipsMove wsl to other diskswsl --export Ubuntu-22.04 D:\wsl\Ubuntu-22.04.tar
wsl --unregister Ubuntu-22.04
wsl --import Ubuntu-22.04 D:\wsl\Ubuntu-22.04 D:\wsl\Ubuntu-22.04.tar --version 2
del D:\wsl\Ubuntu-22.04.tar Config vcxsrv(Deprecated)
sudo service dbus restart
sudo nano ~/.bashrc
export DISPLAY=$(grep -m 1 nameserver /etc/resolv.conf | awk '{print $2}'):0.0
export XDG_SESSION_TYPE=x11
source ~/.bashrc
sudo service dbus restart
gnome-session |
I am considering closing this thread/experiment. Instead don't hesitate to post your issues as new Post/Issue! |
In the spirit of 2020's ImDrawList coding party (#3606) - which led to beautiful stuff like this disco ball and more, I thought about opening a new community thread.
This is a thread where you are encouraged to post your own tip & tricks about using dear imgui.
Are you frequently making use of an clever idiom or trick which saves you time?
Did you just discover some feature that you wish you knew before?
Share them!
Dear ImGui has countless features, flags, which may be hard to discover, or sometimes can be assembled together in creative or unusual ways. While I am trying to make them discoverable via the demo, wiki and variety of threads, I feel that allowing everyone to share their own discoveries in a light-hearted manner may provide for a more attractive read.
Don't hesitate to include pictures/gifs in your post, as they are going to make those tips nicer to adsorb.
The text was updated successfully, but these errors were encountered: