Skip to content
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

Cleanup #219

Closed
ocornut opened this issue May 11, 2015 · 39 comments
Closed

Cleanup #219

ocornut opened this issue May 11, 2015 · 39 comments

Comments

@ocornut
Copy link
Owner

ocornut commented May 11, 2015

I'd like to have a pass of cleanup to

  • reduce lines of code and/or source code size
  • maybe split into multiple files
  • generally make the code more accessible

No specific requirement but generally anything that makes the more simpler helps.

Some metrics from the WIP menus branch:

imgui.h - 1066 LoC
imgui.cpp - 11865 LoC, including:

395 lines: comments, docs
154 lines: includes, forward declarations
481 lines: helpers
7074 lines: main code
395 lines: ImDrawList
1159 lines : ImFontAtlas, ImFont, text rendering, UTF-8 routines
308 lines : platform dependent helpers
200 lines : ShowUserGuide, ShowStyleEditor
1608 lines : ShowTestWindow + micro sample apps
336 : font data + decompressor

This thread is mainly for me noting ideas but if you have feedback please let me know. Personally I don't have a problem with imgui.cpp being so big. But it is important to factor in other people impression (people who may want to work on improving imgui) and also the "portability" of it.. I would prefer to aim to keep the code a little smaller of course, and will work on that. Splitting into more files isn't out of question either.

@extrawurst
Copy link
Contributor

i think the size of imgui.h/cpp is not a concern. i guess right now it is not about to grow in a huge way, so i would say it is not worth splitting it up in multiple files (just my 2c)

@unpacklo
Copy link

A big thing for me personally is making sure it's absolutely trivial to make a part of an existing project/codebase. Preferably, the way it is right now where there is basically no build process at all. I really like being able to just update with:

cp imgui/*.h imgui/*.cpp myprojectimgui/

And building by basically just doing a unity build (although I don't do it on the game I'm working on because of poor choices made early on, my personal projects all do unity builds and its great that your library is easy to use in such a manner!).

If you decide to split things off into multiple files, not such a big deal as long as I can keep doing unity builds; I think it will keep everybody's lives simpler! Dealing with build systems is literally the worst...

@floooh
Copy link
Contributor

floooh commented May 12, 2015

I think at least moving the demo windows into its own file would make sense, that way one can leave them out from the compile process and it's easier to find the demo code for learning the API.

@ocornut
Copy link
Owner Author

ocornut commented May 12, 2015

I know it sounds obvious to split the example code out, but I strongly feel like people need NOT to be encouraged to compile-out the demo window. If it's in its own file the temptation is higher to just remove it from your "project". Then it become unavailable to you and everyone else in a team. It's useful as reference and also to just be able to run it any time.

There's a least 5 or 6 references to ShowTestWindow scattered in the comments + more in the example application and README so hopefully people know where it is. If anything, the demo code is a strong contender to go in another file obviously but I would like to set it up in a way that it is likely to stay in the build.

There is a #define to compile it out btw but that's an explicit act to remove it that rather than requiring an explicit act to add it to your project.

@floooh
Copy link
Contributor

floooh commented May 12, 2015

Ah alright, haven't noticed the define yet. It's also not that big of deal to me, the linker and dead code elimination should take care of not linking the code in the final executable if it is not called :)

@hypernewbie
Copy link

I know the example makes good use of the API, but supporting better docs eg. through Doxygen would be extra-nice.

@jarikomppa
Copy link

Personally I would recommend splitting imgui into several source files for
development, but shipping an unity build of 1-2 files for others to use.

On Tue, May 12, 2015 at 11:01 AM, Andre Weissflog notifications@github.com
wrote:

Ah alright, haven't noticed the define yet. It's also not that big of deal
to me, the linker and dead code elimination should take care of not linking
the code in the final executable if it is not called :)


Reply to this email directly or view it on GitHub
#219 (comment).

@ocornut
Copy link
Owner Author

ocornut commented May 12, 2015

hypernewbie: Yes. Although my plan for documentation is less of a doxygen thing, but more of an illustrated guide with source code on one side and screenshot or animated gif on the other side. Categorized, showing lots of ideas.

jarikomppa: I have no issue for development myself but imgui.cpp could be reorganized, better indexed. In particular all the private stuff that aren't declared in imgui.h are probably less organized than the rest. Having two versions would be additional bureaucracy and confusion (not mentioning the effect on git log/blame).

@jarikomppa
Copy link

omar: I refer to the sqlite distribution - you can add it as hundred files
or you can use the unity build. The separate file solution is easier to
navigate and debug, while the unity build builds faster and is easier to
integrate. You can have it both ways.

On Tue, May 12, 2015 at 11:34 AM, omar notifications@github.com wrote:

hypernewbie: Yes. Although my plan for documentation is less of a doxygen
thing, but more of an illustrated guide with source code on one side and
screenshot or animated gif on the other side. Categorized, showing lots of
ideas.

jarikomppa: I have no issue for development myself but imgui.cpp could
be reorganized, better indexed. In particular all the private stuff that
aren't declared in imgui.h are probably less organized than the rest.
Having two versions would be additional bureaucracy and confusion (not
mentioning the effect on git log/blame).


Reply to this email directly or view it on GitHub
#219 (comment).

@MrSapps
Copy link

MrSapps commented May 17, 2015

I believe gtest does this too, they have the impl in many files but when you include it in your own project or compile to a static lib you only include one gtest_all.cc file which effectively is a unity build.

@Pagghiu
Copy link
Contributor

Pagghiu commented May 18, 2015

Another option (that I currently employ) is to create a .h that includes all necessary .h and a .cpp that includes all necessary .cpp.
You get best of both worlds:

  1. still being able to add the library to a project by just adding a single .cpp and .h
  2. getting the speed of unity build
  3. splitting the files for easier navigation and feature selection
  4. keeping proper git log/blame.

The only difference with the current setup is that you must copy N files instead of 2, but I assume that a person that can code C++ knows how to copy more than 2 files in his source code directory ;-)

FYI there is a library that employs this approach for structuring itself in self contained "modules".
This library is called JUCE, it's very used in the field of music/plugin programming but it's a little jewel of a c++ library that I suggest everyone to take a look at. http://www.juce.com

Each "module" is nothing more than a .cpp that includes all necessary files for a unity build and a .h that includes all headers without the need of header paths or other fancy compiler settings.
Example for core module:

https://github.com/julianstorer/JUCE/tree/master/modules/juce_core

It also has a very nice and simple build generator (visual) tool called Introjucer that can take those modules, together with a super simple .json file that lists all files to be included and generates native ide projects for almost every compiler/platform you like.
The modules are just all those .cpp and .h added automatically and set to build, while the "included" .cpp files are excluded from build.

Example of json file:

https://github.com/julianstorer/JUCE/blob/master/modules/juce_core/juce_module_info

Shot of introjucer
http://www.juce.com/sites/default/files/Introjucer.png

Auto generating build files is probably not worth the effort for just one example on many platforms.
If examples will become more and more, it could make sense, but not now in my opinion.

@ocornut
Copy link
Owner Author

ocornut commented May 18, 2015

The main question is mostly to ask IF ImGui needs to be split into multiple files, and HOW which I don't have a trivial answer for. How they are actually compiled is a smaller detail. At the moment I was most looking forward to work on simplifying the source code rather than splitting files.

I didn't even realize that JUCE what open-source (unfortunately the free version it is GPL). I have bookmarked a few JUCE pages for reference of features I'd like to implement. I mean just look at https://www.juce.com/api/classSlider.html

I was actually thinking of auto generated build files for the examples. So strictly speaking not part of core imgui, but we need it to be able to ship example for different high-level libraries that are optional. Right now there's a single .sln for visual studio so we can't easily have optional projects there. That's a separate topic to the cleanup.

@Pagghiu
Copy link
Contributor

Pagghiu commented May 18, 2015

Oh, sure, I'm totally with you, simplifying the source code is way more valuable than splitting files!
JUCE is one of the most elegant and effective pieces of C++ software I've ever been reading.
I think that such a clearness in style and design can only come from a single developer, the impressive bit is that he was able to do everything himself (afaik) with such a broad range of topics ranging from multiplatform Graphics and GUI systems, to build systems and much more.
There's even and handmade Javascript interpreter in ~ 1000-ish lines of code!

Coming back in topic, unfortunately I've not spent time to study current IMGUI source code to give a specific hint or a pull request for the cleanup but these are the very first steps that I do for cleanup/refactoring:

  1. First pass of duplicate finding, trying to refactor pieces of code that were copy/pasted (if any).
  2. Look at long-ish functions (more than 30 lines of code) and try to group them into an actual "action" that can be easily described by a its function name, even if it's used just in one place.
    In this way once longer functions become short and high level do-this-than-do-that description of an algorithm rather than a jungle of if(!this && !that) doSomething().

If you want build files for examples, we could use Introjucer for generating them!
Introjucer can be used even without using JUCE at all.
Not sure if there is any interference with the software itself being GPL/commercial, as we will be USING it, not embedding its code somewhere. The only drawback is that it creates a JuceLibraryCode for the modules (can be changed recompiling introjucer but triggers the gpl...).

my 2 cents!

@unpacklo
Copy link

Honestly, I'm a huge fan if the way you've been writing the code so far. Although it's largely a matter of taste, there's something to be said about the simplicity you've already managed to achieve/maintain in the code. I rather enjoy not having to hunt through various files to see where the implementation of something is, and as a person who edits code in emacs with very little additional assistance, I find this to be a pretty big win (although, emacs dies a little sometimes with the large imgui.cpp in cpp mode).

Seems better to me to leave the implementation single file for now. But something I would like some consideration on is splitting the header file (or at least rearrangement). I haven't looked very carefully at the dependencies on definitions, but I feel like you could reorg the header in such a way that somebody could look at the beginning of just one file that held the 90% case (window setup, widgets, input control, etc) and then throw in some of the more advanced feature API into another header (or further down in the header). This would make it easier for those new to the library to just look at one place that will get them the basics done quickly and if they want to branch out into more custom things, they can look elsewhere which has the rest.

So more concretely, we have the ImGui namespace starting in the header file at line

imgui/imgui.h

Line 155 in b6f3c97

namespace ImGui
which is far past the first screen's worth of text for me at 1920x1080. It would be nice to have the namespace start pretty much smack at the start of the file so newcomers can see it immediately and get to work.

Also, the 90% case:

imgui/imgui.h

Lines 169 to 175 in b6f3c97

// See implementation in .cpp for details
IMGUI_API bool Begin(const char* name = "Debug", bool* p_opened = NULL, ImGuiWindowFlags flags = 0); // return false when window is collapsed, so you can early out in your code. 'bool* p_opened' creates a widget on the upper-right to close the window (which sets your bool to false).
IMGUI_API bool Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags = 0); // this is the older/longer API. call SetNextWindowSize() instead if you want to set a window size. For regular windows, 'size_on_first_use' only applies to the first time EVER the window is created and probably not what you want! maybe obsolete this API eventually.
IMGUI_API void End();
IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400).
IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // "
IMGUI_API void EndChild();

imgui/imgui.h

Lines 275 to 362 in b6f3c97

// Widgets
IMGUI_API void Text(const char* fmt, ...);
IMGUI_API void TextV(const char* fmt, va_list args);
IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();
IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args);
IMGUI_API void TextWrapped(const char* fmt, ...); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();
IMGUI_API void TextWrappedV(const char* fmt, va_list args);
IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // doesn't require null terminated string if 'text_end' is specified. no copy done to any bounded stack buffer, recommended for long chunks of text
IMGUI_API void LabelText(const char* label, const char* fmt, ...); // display text+label aligned the same way as value+label widgets
IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args);
IMGUI_API void Bullet();
IMGUI_API void BulletText(const char* fmt, ...);
IMGUI_API void BulletTextV(const char* fmt, va_list args);
IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0), bool repeat_when_held = false);
IMGUI_API bool SmallButton(const char* label);
IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size);
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0));
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,1), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
IMGUI_API bool CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false);
IMGUI_API bool Checkbox(const char* label, bool* v);
IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value);
IMGUI_API bool RadioButton(const char* label, bool active);
IMGUI_API bool RadioButton(const char* label, int* v, int v_button);
IMGUI_API bool Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1);
IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items = -1); // separate items with \0, end item-list with \0\0
IMGUI_API bool Combo(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1);
IMGUI_API bool ColorButton(const ImVec4& col, bool small_height = false, bool outline_border = true);
IMGUI_API bool ColorEdit3(const char* label, float col[3]);
IMGUI_API bool ColorEdit4(const char* label, float col[4], bool show_alpha = true);
IMGUI_API void ColorEditMode(ImGuiColorEditMode mode);
IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float));
IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0));
IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float));
IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0));
// Widgets: Sliders (tip: ctrl+click on a slider to input text)
IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders
IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f);
IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f");
IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f");
IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f");
IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f");
IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f");
// Widgets: Drags (tip: ctrl+click on a drag box to input text)
IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound
IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f);
IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f);
IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f);
IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound
IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f");
IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f");
IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f");
// Widgets: Input
IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0);
// Widgets: Trees
IMGUI_API bool TreeNode(const char* str_label_id); // if returning 'true' the node is open and the user is responsible for calling TreePop
IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...); // "
IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...); // "
IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args); // "
IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args); // "
IMGUI_API void TreePush(const char* str_id = NULL); // already called by TreeNode(), but you can call Push/Pop yourself for layouting purpose
IMGUI_API void TreePush(const void* ptr_id = NULL); // "
IMGUI_API void TreePop();
IMGUI_API void SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond = 0); // set next tree node to be opened.
// Widgets: Selectable / Lists
IMGUI_API bool Selectable(const char* label, bool selected = false, const ImVec2& size = ImVec2(0,0));
IMGUI_API bool Selectable(const char* label, bool* p_selected, const ImVec2& size = ImVec2(0,0));
IMGUI_API bool ListBox(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1);
IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1);
IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. make sure to call ListBoxFooter() afterwards.
IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // "
IMGUI_API void ListBoxFooter(); // terminate the scrolling region

imgui/imgui.h

Lines 385 to 390 in b6f3c97

// Utilities
IMGUI_API bool IsItemHovered(); // was the last item hovered by mouse?
IMGUI_API bool IsItemHoveredRect(); // was the last item hovered by mouse? even if another item is active while we are hovering this
IMGUI_API bool IsItemActive(); // was the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false)
IMGUI_API bool IsAnyItemActive(); //
IMGUI_API bool IsItemVisible();

imgui/imgui.h

Lines 398 to 406 in b6f3c97

IMGUI_API bool IsKeyDown(int key_index); // key_index into the keys_down[512] array, imgui doesn't know the semantic of each entry
IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // "
IMGUI_API bool IsMouseDown(int button);
IMGUI_API bool IsMouseClicked(int button, bool repeat = false);
IMGUI_API bool IsMouseDoubleClicked(int button);
IMGUI_API bool IsMouseHoveringWindow(); // is mouse hovering current window ("window" in API names always refer to current window)
IMGUI_API bool IsMouseHoveringAnyWindow(); // is mouse hovering any active imgui window
IMGUI_API bool IsMouseHoveringRect(const ImVec2& rect_min, const ImVec2& rect_max);// is mouse hovering given bounding rect
IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold

I find myself using these the most, by far. The organization you already have I'm a very big fan of (grouping up by category), so splitting them off more and moving them is probably a loss, but I think at least putting the namespace at the beginning of the file would be awesome.

@ocornut
Copy link
Owner Author

ocornut commented May 18, 2015

imgui.h is already designed to show the main stuff as high as possible, but..For some reason I was convinced that ImVector<> needed to be at the top for some reason, however it appears that it isn't necessary (maybe it was in 1.0?), so I moved it below the main namespace in da53caf. The main namespace now starts at line 90. Thanks! I'm not sure we can squeeze it more.

It's sort of debatable which should are the top most used functions. I think few people uses the input functions (IsKeyDown, etc.) and the other utilities you highlighted such as IsItemHovered() aren't that used in the simplest use case. And we could argue that GetIO/NewFrame/Render etc. even though they aren't useful once you are already setup, it's very valuable to see them at the top the first time. So I wouldn't really know how to re-order that block. Perhaps everything after the blocks of Begin/End/BeginChild/EndChild could go after the widgets? I think it may look less structured this way?

@unpacklo
Copy link

Yeah it's definitely tough, you can't pick to optimize everything for everyone... But all things considered, I think the overall order within the namespace is actually quite good.

The only way I can think of to bring the namespace closer to the top is to pull it out into another file but I'm not sure that's a good enough reason for a whole extra file. Or maybe put the section of includes, forward decls and typedefs into their own header and include it into imgui.h (again, a whole extra file) to compress down the text that appears before the namespace.

Dale Kim
Programmer at Stellar Jockeys

On May 18, 2015, at 5:20 PM, omar notifications@github.com wrote:

imgui.h is already designed to show the main stuff as high as possible, but..For some reason I was convinced that ImVector<> needed to be at the top for some reason, however it appears that it isn't necessary (maybe it was in 1.0?), so I moved it below the main namespace in da53caf. The main namespace now starts at line 90. Thanks! I'm not sure we can squeeze it more.

It's sort of debatable which should are the top most used functions. I think few people uses the input functions (IsKeyDown, etc.) and the other utilities you highlighted such as IsItemHovered() aren't that used in the simplest use case. And we could argue that GetIO/NewFrame/Render etc. even though they aren't useful once you are already setup, it's very valuable to see them at the top the first time. So I wouldn't really know how to re-order that block. Perhaps everything after the blocks of Begin/End/BeginChild/EndChild could go after the widgets? I think it may look less structured this way?


Reply to this email directly or view it on GitHub.

@ocornut
Copy link
Owner Author

ocornut commented Jun 29, 2015

I have made a first local (uncommitted) attempt a splitting imgui.cpp into several files just to see the issues I would get into.

29/06/2015  09:59           295,282 imgui.cpp               7182 lines (from about 12800)
26/06/2015  20:48            90,852 imgui.h                 1164 lines
26/06/2015  21:32            85,780 imgui_demo.cpp          1990 lines
29/06/2015  09:47            79,506 imgui_font.cpp          1389 lines
29/06/2015  09:48            33,082 imgui_internal.h        589 lines
29/06/2015  09:59            35,263 imgui_misc.cpp          1079 lines
29/06/2015  09:49            38,072 imgui_textedit.cpp      763 lines

Not really happy with that. imgui.cpp still too big. splitting up e.g. imgui_textedit.cpp not really worth it considering it requires dragging lots of things out. Feeling with with not having one .h per .cpp. I don't have an obvious natural way of splitting imgui.cpp further, there's no oop style stuff left in imgui.cpp that are natural groups of important size. I could split it but it'd be very either very arbitrary, either it would have to be split in very small files (which to me is undesirable).

I think imgui_demo.cpp is the most obvious thing to separate (as mentioned above I am only reluctant to split it because I don't want people to compile it out).

My idea right now is that will revert most of that and want to focus on building a cleaner, near exhaustive version of imgui_internal.h. The effects of that would be:

  • You can use the internal stuff without relying on the wonky inline include. (but use of internal stuff won't be forward-compatibility guaranteed)
  • Will shave off about a thousand lines from imgui.cpp as a bonus
  • Few functions will be static (they will probably be in a namespace instead) so things can be split into different files more easily.
  • Even half-exposing those functions will enforce more cleanup, better naming, more comments.
  • Reading imgui_internal.h will be a big help toward understanding the inner structure of the library. That's probably where more comments will reside and serve as introductory documentation to the innards.
  • We can make imgui.cpp more strictly follow the ordering/grouping of thing as presented in imgui_internal.h it'll be easier to navigate.

So my intuition is that we will end up with

imgui.cpp
imgui.h            ~1000
imgui_demo.cpp     ~2000
imgui_internal.h   ~1000

I will intentionally leave some dependencies between imgui.cpp and imgui_demo.cpp to ensure it's not compiled out :)

@ocornut
Copy link
Owner Author

ocornut commented Jun 29, 2015

More musing. Tried the above and not satisfied either.

I can't take a imgui_demo.cpp out without introducing a imgui_internal.h because the earlier uses several simple helpers. They are all generic helpers like ImStricmp, etc. It would make some sense to bother rewriting that function without those helpers because ShowTestWindow() being demo code it's preferable to allow it to be copy & pasted. Here's the full list used in ShowTestWindow().

Error   2   error C2676: binary '+' : 'ImVec2' does not define this operator or a conversion to a type acceptable to the predefined operator    D:\T-Work\GitHub\imgui\imgui_demo.cpp   234
Error   6   error C3861: 'IM_ARRAYSIZE': identifier not found   D:\T-Work\GitHub\imgui\imgui_demo.cpp   2624
Error   50  error C3861: 'ImCharIsSpace': identifier not found  D:\T-Work\GitHub\imgui\imgui_demo.cpp   1608
Error   9   error C3861: 'ImClamp': identifier not found    D:\T-Work\GitHub\imgui\imgui_demo.cpp   292
Error   39  error C3861: 'ImFormatString': identifier not found D:\T-Work\GitHub\imgui\imgui_demo.cpp   1363
Error   43  error C3861: 'ImFormatStringV': identifier not found    D:\T-Work\GitHub\imgui\imgui_demo.cpp   1471
Error   198 error C3861: 'ImMax': identifier not found  D:\T-Work\GitHub\imgui\imgui_demo.cpp   1397
Error   46  error C3861: 'ImStricmp': identifier not found  D:\T-Work\GitHub\imgui\imgui_demo.cpp   1557
Error   207 error C3861: 'ImStrnicmp': identifier not found D:\T-Work\GitHub\imgui\imgui_demo.cpp   1616

Here's a list of all the generic helpers currently in imgui.cpp

// Helpers: String
IMGUI_API int               ImStricmp(const char* str1, const char* str2);
IMGUI_API int               ImStrnicmp(const char* str1, const char* str2, int count);
IMGUI_API char*             ImStrdup(const char* str);
IMGUI_API size_t            ImStrlenW(const ImWchar* str);
IMGUI_API const ImWchar*    ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
IMGUI_API const char*       ImStristr(const char* haystack, const char* needle, const char* needle_end);
IMGUI_API size_t            ImFormatString(char* buf, size_t buf_size, const char* fmt, ...);
IMGUI_API size_t            ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args);

// Helpers: Misc
IMGUI_API ImU32             ImHash(const void* data, size_t data_size, ImU32 seed); // Currently CRC32
IMGUI_API bool              ImLoadFileToMemory(const char* filename, const char* file_open_mode, void** out_file_data, size_t* out_file_size, size_t padding_bytes = 0);
static inline int           ImUpperPowerOfTwo(int v)    { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
static inline bool          ImCharIsSpace(int c)       { return c == ' ' || c == '\t' || c == 0x3000; }

// Helpers: UTF-8 <> wchar
IMGUI_API int               ImTextCharToUtf8(char* buf, size_t buf_size, unsigned int in_char);                                // return output UTF-8 bytes count
IMGUI_API ptrdiff_t         ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_text, const ImWchar* in_text_end);   // return output UTF-8 bytes count
IMGUI_API int               ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);          // return input UTF-8 bytes count
IMGUI_API ptrdiff_t         ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL);   // return input UTF-8 bytes count
IMGUI_API int               ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end);                            // return number of UTF-8 code-points (NOT bytes count)
IMGUI_API int               ImTextCountUtf8BytesFromChar(unsigned int in_char);                                                // return output UTF-8 bytes count
IMGUI_API int               ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end);                   // return number of bytes to express string as UTF-8 code-points

// Helpers: Maths
static inline int           ImMin(int lhs, int rhs)                                    { return lhs < rhs ? lhs : rhs; }
static inline int           ImMax(int lhs, int rhs)                                    { return lhs >= rhs ? lhs : rhs; }
static inline float         ImMin(float lhs, float rhs)                                { return lhs < rhs ? lhs : rhs; }
static inline float         ImMax(float lhs, float rhs)                                { return lhs >= rhs ? lhs : rhs; }
static inline ImVec2        ImMin(const ImVec2& lhs, const ImVec2& rhs)                { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); }
static inline ImVec2        ImMax(const ImVec2& lhs, const ImVec2& rhs)                { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); }
static inline int           ImClamp(int v, int mn, int mx)                             { return (v < mn) ? mn : (v > mx) ? mx : v; }
static inline float         ImClamp(float v, float mn, float mx)                       { return (v < mn) ? mn : (v > mx) ? mx : v; }
static inline ImVec2        ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx)      { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); }
static inline float         ImSaturate(float f)                                        { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
static inline float         ImLerp(float a, float b, float t)                          { return a + (b - a) * t; }
static inline ImVec2        ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t)  { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
static inline float         ImLengthSqr(const ImVec2& lhs)                             { return lhs.x*lhs.x + lhs.y*lhs.y; }
static inline float         ImLengthSqr(const ImVec4& lhs)                             { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; }
IMGUI_API bool              ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c);

// Helpers: Vector Maths operators
// Disabled by default so user can include imgui_internal.h and still use the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to define conversions from ImGui types to their native types.
#ifdef IMGUI_DEFINE_MATH_OPERATORS
// We are keeping those static in the .cpp file so as not to leak them outside, in the case the user has implicit cast operators between ImVec2 and its own types.
static inline ImVec2 operator*(const ImVec2& lhs, const float rhs)              { return ImVec2(lhs.x*rhs, lhs.y*rhs); }
static inline ImVec2 operator/(const ImVec2& lhs, const float rhs)              { return ImVec2(lhs.x/rhs, lhs.y/rhs); }
static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs)            { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); }
static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs)            { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); }
static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2 rhs)             { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); }
static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2 rhs)             { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); }
static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs)                { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }
static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs)                { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }
static inline ImVec2& operator*=(ImVec2& lhs, const float rhs)                  { lhs.x *= rhs; lhs.y *= rhs; return lhs; }
static inline ImVec2& operator/=(ImVec2& lhs, const float rhs)                  { lhs.x /= rhs; lhs.y /= rhs; return lhs; }
static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs)            { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-lhs.w); }
#endif

And list of most of the remaining private/static functions of imgui,cpp.
I don't think all of them make sense to be exposed even in imgui_internal.h, perhaps 50% of them in which case I'll have to put some efforts into organising/renaming them.

// Render
void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, ImGuiAlign align = ImGuiAlign_Default, const ImVec2* clip_min = NULL, const ImVec2* clip_max = NULL);
void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
void          RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale = 1.0f, bool shadow = false);
void          RenderCheckMark(ImVec2 pos, ImU32 col);

// Window
inline ImGuiWindow* GetCurrentWindow();
inline void         SetCurrentWindow(ImGuiWindow* window);
inline ImGuiWindow* GetParentWindow();
ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
ImGuiWindow* FindWindowByName(const char* name)
ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
void         SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
void         SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond)
void         SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond)
void         SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond)
inline bool  IsWindowContentHoverable(ImGuiWindow* window)
void         ClearSetNextWindowData()
void         FocusWindow(ImGuiWindow* window)

void         CheckStacksSize(ImGuiWindow* window, bool write)

void         SetActiveId(ImGuiID id, ImGuiWindow* window = NULL);
void         RegisterAliveId(ImGuiID id);

ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImU32 key);

void         PushClipRect(const ImVec4& clip_rect, bool clipped = true)
void         PopClipRect()
ImVec4       GetVisibleRect()

// Settings
ImGuiIniData* FindWindowSettings(const char* name)
ImGuiIniData* AddWindowSettings(const char* name)
void         LoadSettings()
void         SaveSettings()
void         MarkSettingsDirty()

void         LogText(const ImVec2& ref_pos, const char* text, const char* text_end)
float        CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
const char*  FindTextDisplayEnd(const char* text, const char* text_end = NULL)

bool         IsPopupOpen(ImGuiID id)
void         CloseInactivePopups()
ImGuiWindow* GetFrontMostModalRootWindow()
void         ClosePopupToLevel(int remaining)
void         ClosePopup(ImGuiID id)
bool         BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)
ImVec2       FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, ImGuiWindowFlags flags, int* last_dir, const ImRect& r_inner)

void         PushMultiItemsWidths(int components, float w_full = 0.0f)

void         SetFont(ImFont* font)
float*       GetStyleVarFloatAddr(ImGuiStyleVar idx)
ImVec2*      GetStyleVarVec2Addr(ImGuiStyleVar idx)

void         Scrollbar(ImGuiWindow* window)
bool         CloseWindowButton(bool* p_opened)

bool         ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0)
bool         ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, ImGuiButtonFlags flags)
bool         DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
bool         SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, bool horizontal)

bool         SliderFloatAsInputText(const char* label, float* v, ImGuiID id, int decimal_precision)
inline void  ParseFormatPrecision(const char* fmt, int& decimal_precision)
inline float RoundScalar(float value, int decimal_precision)
void         Plot(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)

bool         SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
bool         SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
bool         DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
bool         DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
bool         InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
bool         InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)

void         InputTextApplyArithmeticOp(const char* buf, float *v)
int          InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
ImVec2       InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false)
bool         InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
bool         InputTextEx(const char* label, char* buf, size_t buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)

void         ItemSize(ImVec2 size, float text_offset_y)
inline void  ItemSize(const ImRect& bb, float text_offset_y)
bool         ItemAdd(const ImRect& bb, const ImGuiID* id)
bool         IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false)
bool         IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)

float        GetDraggedColumnOffset(int column_index)
void         PushColumnClipRect(int column_index)

Realistically I think this dozen below covers 98% of the use cases to create new widget with the current style. But arguably the code needs a few more helpers to make the widget code simpler.

ImGuiWindow* GetCurrentWindow();
void         FocusWindow(ImGuiWindow* window)
void         SetActiveId(ImGuiID id, ImGuiWindow* window = NULL);

bool         ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0)
bool         ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, ImGuiButtonFlags flags)
bool         DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
bool         SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, bool horizontal)

void         ItemSize(ImVec2 size, float text_offset_y)
inline void  ItemSize(const ImRect& bb, float text_offset_y)
bool         ItemAdd(const ImRect& bb, const ImGuiID* id)
bool         IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false)
bool         IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)

So even thou introducing imgui_internal.h will be of use and I'm not against it I don't think it'll constitute much of a meaningful cleanup. If I want to "index" the content of imgui.cpp I can do that at the top of imgui.cpp

Perhaps I could move ImDrawList along with ImFont and ImFontAtlas + the font data into its own file, that would be an extra 2 k.

@adamdmoss
Copy link
Contributor

I agree that it's highly desirable that the demo code not use internal APIs, and it's a bonus if that helps with a splitting.

@emoon
Copy link
Contributor

emoon commented Jun 30, 2015

Maybe you can have something like imgui_utils.cpp/h for your string/vector code?

@ocornut
Copy link
Owner Author

ocornut commented Jul 19, 2015

I can get imgui_demo.cpp out now. Not committed because if I add new files I'd rather do it all once.

I thought about including the other files from imgui.cpp which would facilitate development but I can't think of a way to make it "non weird" and it may be more confusing for the user, and I don't want user to be browsing the extra files with their IDE showing them as disabled code because of some ifdef hackery. If they were called .inl it would be more compatible with build systems (more likely to show but not build directly), but I'd rather go for the normal thing, call them .cpp and require the user to add them? Less confusing.

That's where I am now

(A)
imgui.cpp
imgui.h
imgui_demo.cpp // all demo code
imgui_draw.cpp // imDrawList, ImFontAtlas, ImFont, font data + some drawing helpers
imgui_internals.h
imgui_utils.cpp ?

We've got an imgui.cpp that is still 7000+ lines here (instead of 13500).

(B)
imgui.cpp
imgui.h
imgui_internals.h
imgui_demo.cpp

I think it'll go with something like either of those. The only things that's making me uncomfortable is the jump to add more .cpp files but once that's done it's done. I just want to settle on a good initial configuration so the files won't change every few months.

@dumblob
Copy link

dumblob commented Jul 19, 2015

From my point of view, the most important change is to separate the demo code to it's own file. The second important change is to separate pure drawing backend (i.e. no API) to it's own file. The rest is solely on your own decision (but as few files as possible might be a good metric for decision making).

So the solution (A) would be an option, even though I myself consider imgui_internals.h and imgui_utils.cpp files needless - why not to leave their content in imgui.cpp and imgui.h respectively?

@emoon
Copy link
Contributor

emoon commented Jul 19, 2015

I like having an imgui_internal.h file because then implementation details will not 'leak' into the main imgui.h file (or ar less likely to do so) I vote (A)

@ocornut
Copy link
Owner Author

ocornut commented Jul 21, 2015

Here's where I am presently:

imgui.cpp 8850 lines
imgui.h 1250 lines
imgui_demo.cpp 2050 lines
imgui_draw.cpp 2140 lines
imgui_internal.h 590 lines

imgui_internal.h is currently only exposing the data structure and a few helper functions but not all the private functions of imgui.cpp (less than 50, but that is the natural followup of events to add them to the .h file). Adding this file is pretty important/useful as it is enable smoother extensions even if they are sitting on unsupported API (from there we can promote bits to the public API).

I'm ok with that split but imgui.cpp is still 8850 lines (compared to 13500).
Considering that I started this split to make it more welcoming to people, I wonder if maybe it's not enough and I should consider splitting it further? Apart from the utilities (<1000 lines) I don't see a natural split at this point.

@emoon
Copy link
Contributor

emoon commented Jul 21, 2015

I guess if you want to split more then perhaps consider groups of widgets like

imgui_sliders
imgui_tree
imgui_drag
...

@ocornut
Copy link
Owner Author

ocornut commented Jul 21, 2015

Sliders are 430 lines including some helpers.
The largest are InputText and the cruft surrounding it and that's 750 lines, 900 lines if I include InputFloat, InputInt.
Menus I wouldn't be able to gather more than 500 lines (lots of smaller things are scattered in generic helpers)

If I go this way it's either lots of 300-800 lines files either rather arbitrary separation and personally I don't fancy either.

I'm forward-declaring all the static functions of imgui.cpp at the top, help sortng things out.

@tamaskenez
Copy link

The two other projects I know about where they provide source files to directly add to client projects are SQLite and JUCE. Both work with hundreds of files for development and provide a single-file amalgamation. Maybe that's a good idea for ImGui, too.

Jules even has a nice open-sourced amalgamation tool, I used it for my own projects.

Edit: I mean, my point is that you can have two different levels of splitting, a max-level which is good for development and another one which is easy to link with.

@extrawurst
Copy link
Contributor

btw. @ocornut if you release this 'cleanup', could you try to limit this release to the new file structure and not mix it with various API changes ? This would simplify porting a lot :)

@extrawurst
Copy link
Contributor

i don't see this happening in a branch right now, is that intended ?

@ocornut
Copy link
Owner Author

ocornut commented Jul 21, 2015

Yes it's local until I find a configuration that I like. I'll probably release 1.44 with this and the few minor changes done since 1.43.

@extrawurst
Copy link
Contributor

ok great. can you please list any API changes explicitly then, to simplify porting ?

@ocornut
Copy link
Owner Author

ocornut commented Jul 21, 2015

Yes. They should always be explicitly listed in the "release note" log.

@ocornut
Copy link
Owner Author

ocornut commented Jul 21, 2015

..and you'll notice that imgui.h won't have changed at all (apart from some comment)
Whatever is in imgui_internal.h you probably DON'T want to expose in your c-api. Those are really the non-public stuff. They need to be exposed somewhere to split the code in different cpp files and I'm sure some people will use them but there's no point in porting or maintaining them.

@ocornut
Copy link
Owner Author

ocornut commented Aug 2, 2015

If you are curious to have a look at this branch.

imgui_internal.h is exposing

  • all data structures
  • various helper functions (maths, utf8, string stuff)
  • ImVec2/ImVec4 operators ONLY if IMGUI_DEFINE_MATH_OPERATORS is defined
  • and the following internal ImGui functions:
//-----------------------------------------------------------------------------
// Internal API
// No guarantee of forward compatibility here.
//-----------------------------------------------------------------------------

namespace ImGui
{
bool          ItemAdd(const ImRect& bb, const ImGuiID* id);
void          ItemSize(const ImVec2& size, float text_offset_y = 0.0f);
void          ItemSize(const ImRect& bb, float text_offset_y = 0.0f);
bool          IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged);
bool          IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false);
bool          FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true);      // Return true if focus is requested
void          FocusableItemUnregister(ImGuiWindow* window);
ImVec2        CalcItemSize(ImVec2 size, float default_x, float default_y);
float         CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);

void          SetActiveID(ImGuiID id, ImGuiWindow* window);
void          KeepAliveID(ImGuiID id);

ImGuiWindow*  GetCurrentWindow();
ImGuiWindow*  GetParentWindow();
void          FocusWindow(ImGuiWindow* window);

void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, ImGuiAlign align = ImGuiAlign_Default, const ImVec2* clip_min = NULL, const ImVec2* clip_max = NULL);
void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
void          RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale = 1.0f, bool shadow = false);
void          RenderCheckMark(ImVec2 pos, ImU32 col);

bool          ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, ImGuiButtonFlags flags = 0);
bool          ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0);

bool          SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, bool horizontal);
bool          SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power);
bool          SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format);

bool          DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power);
bool          DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power);
bool          DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format);

bool          InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);
bool          InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags);
bool          InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags);
bool          InputFloatReplaceWidget(const ImRect& aabb, const char* label, float* v, ImGuiID id, int decimal_precision);

void          PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size);

int           ParseFormatPrecision(const char* fmt, int default_value);
float         RoundScalar(float value, int decimal_precision);
}

The count is:

imgui.cpp 8853 lines
imgui.h 1249 lines
imgui_internal.h 658 lines
imgui_draw.cpp 2104 lines
imgui_demo.cpp 2054 lines

The presence of data structures and most useful functions in imgui_internal.h I believe makes imgui.cpp more approachable.

Any feedback of what could be done next, or are we happy with this?

@Pagghiu
Copy link
Contributor

Pagghiu commented Aug 2, 2015

This is the same as I would have been structuring it, I like it 👍

ocornut added a commit that referenced this issue Aug 5, 2015
ocornut added a commit that referenced this issue Aug 5, 2015
@ocornut
Copy link
Owner Author

ocornut commented Aug 6, 2015

Merged in master!

@emoon
Copy link
Contributor

emoon commented Aug 6, 2015

👍

ocornut added a commit that referenced this issue Aug 6, 2015
@bkaradzic
Copy link
Contributor

👍

When you have sane build system, it doesn't matter do you have one file, or many files. I just upgraded ImGui in bgfx and this is what changed: bkaradzic/bgfx@437656b

IMHO, only people with crazy build setup (for example, those that manually f* around with .vcproj/.sln files) prefer single header file...

@ocornut
Copy link
Owner Author

ocornut commented Aug 8, 2015

@bkaradzic I'm not happy with relying on the assumption of having a "sane build system". Using Visual Studio project system is still the easiest way to get started. ImGui is meant to be inclusive in the sense that you can add it to your 1-weekend experiment.

Anyway this is done and we're not turning back :)

I'll close this thread - not to say that there won't be more cleanup, but I think it got already way more sane!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests