@@ -152,6 +152,9 @@ namespace IGFD
152152#ifndef tableHeaderFileNameString
153153#define tableHeaderFileNameString " File name"
154154#endif // tableHeaderFileNameString
155+ #ifndef tableHeaderFileTypeString
156+ #define tableHeaderFileTypeString " Type"
157+ #endif // tableHeaderFileTypeString
155158#ifndef tableHeaderFileSizeString
156159#define tableHeaderFileSizeString " Size"
157160#endif // tableHeaderFileSizeString
@@ -512,7 +515,7 @@ namespace IGFD
512515 // ////////////////////////////////////////////////////////////////////////////////////////////////
513516 // /// CUSTOM SELECTABLE (Flashing Support) ///////////////////////////////////////////////////////
514517 // ////////////////////////////////////////////////////////////////////////////////////////////////
515-
518+
516519#ifdef USE_EXPLORATION_BY_KEYS
517520 bool IGFD::FileDialog::FlashableSelectable (const char * label, bool selected,
518521 ImGuiSelectableFlags flags, bool vFlashing, const ImVec2& size_arg)
@@ -526,62 +529,79 @@ namespace IGFD
526529 ImGuiContext& g = *GImGui;
527530 const ImGuiStyle& style = g.Style ;
528531
529- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC .CurrentColumns ) // FIXME-OPT: Avoid if vertically clipped.
530- PushColumnsBackground ();
531-
532532 // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
533533 ImGuiID id = window->GetID (label);
534534 ImVec2 label_size = CalcTextSize (label, NULL , true );
535- ImVec2 size (
536- IS_FLOAT_DIFFERENT (size_arg.x , 0 .0f ) ? size_arg.x : label_size.x ,
537- IS_FLOAT_DIFFERENT (size_arg.y , 0 .0f ) ? size_arg.y : label_size.y
538- );
535+ ImVec2 size (size_arg.x != 0 .0f ? size_arg.x : label_size.x , size_arg.y != 0 .0f ? size_arg.y : label_size.y );
539536 ImVec2 pos = window->DC .CursorPos ;
540537 pos.y += window->DC .CurrLineTextBaseOffset ;
541538 ItemSize (size, 0 .0f );
542539
543540 // Fill horizontal space
544- const float min_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ContentRegionRect .Min .x : pos.x ;
545- const float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ContentRegionRect .Max .x : GetContentRegionMaxAbs ().x ;
546- if (IS_FLOAT_DIFFERENT (size_arg.x , 0 .0f ) || (flags & ImGuiSelectableFlags_SpanAvailWidth))
541+ // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitely right-aligned sizes not visibly match other widgets.
542+ const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0 ;
543+ const float min_x = span_all_columns ? window->ParentWorkRect .Min .x : pos.x ;
544+ const float max_x = span_all_columns ? window->ParentWorkRect .Max .x : window->WorkRect .Max .x ;
545+ if (size_arg.x == 0 .0f || (flags & ImGuiSelectableFlags_SpanAvailWidth))
547546 size.x = ImMax (label_size.x , max_x - min_x);
548547
549548 // Text stays at the submission position, but bounding box may be extended on both sides
550549 const ImVec2 text_min = pos;
551550 const ImVec2 text_max (min_x + size.x , pos.y + size.y );
552551
553552 // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
554- ImRect bb_enlarged (min_x, pos.y , text_max.x , text_max.y );
555- const float spacing_x = style.ItemSpacing .x ;
556- const float spacing_y = style.ItemSpacing .y ;
557- const float spacing_L = IM_FLOOR (spacing_x * 0 .50f );
558- const float spacing_U = IM_FLOOR (spacing_y * 0 .50f );
559- bb_enlarged.Min .x -= spacing_L;
560- bb_enlarged.Min .y -= spacing_U;
561- bb_enlarged.Max .x += (spacing_x - spacing_L);
562- bb_enlarged.Max .y += (spacing_y - spacing_U);
563- // if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb_align.Min, bb_align.Max, IM_COL32(255, 0, 0, 255)); }
564- // if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb_enlarged.Min, bb_enlarged.Max, IM_COL32(0, 255, 0, 255)); }
553+ ImRect bb (min_x, pos.y , text_max.x , text_max.y );
554+ if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0 )
555+ {
556+ const float spacing_x = span_all_columns ? 0 .0f : style.ItemSpacing .x ;
557+ const float spacing_y = style.ItemSpacing .y ;
558+ const float spacing_L = IM_FLOOR (spacing_x * 0 .50f );
559+ const float spacing_U = IM_FLOOR (spacing_y * 0 .50f );
560+ bb.Min .x -= spacing_L;
561+ bb.Min .y -= spacing_U;
562+ bb.Max .x += (spacing_x - spacing_L);
563+ bb.Max .y += (spacing_y - spacing_U);
564+ }
565+ // if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
566+
567+ // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable..
568+ const float backup_clip_rect_min_x = window->ClipRect .Min .x ;
569+ const float backup_clip_rect_max_x = window->ClipRect .Max .x ;
570+ if (span_all_columns)
571+ {
572+ window->ClipRect .Min .x = window->ParentWorkRect .Min .x ;
573+ window->ClipRect .Max .x = window->ParentWorkRect .Max .x ;
574+ }
565575
566576 bool item_add;
567577 if (flags & ImGuiSelectableFlags_Disabled)
568578 {
569579 ImGuiItemFlags backup_item_flags = window->DC .ItemFlags ;
570580 window->DC .ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus;
571- item_add = ItemAdd (bb_enlarged , id);
581+ item_add = ItemAdd (bb , id);
572582 window->DC .ItemFlags = backup_item_flags;
573583 }
574584 else
575585 {
576- item_add = ItemAdd (bb_enlarged , id);
586+ item_add = ItemAdd (bb , id);
577587 }
578- if (!item_add)
588+
589+ if (span_all_columns)
579590 {
580- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC .CurrentColumns )
581- PopColumnsBackground ();
582- return false ;
591+ window->ClipRect .Min .x = backup_clip_rect_min_x;
592+ window->ClipRect .Max .x = backup_clip_rect_max_x;
583593 }
584594
595+ if (!item_add)
596+ return false ;
597+
598+ // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
599+ // which would be advantageous since most selectable are not selected.
600+ if (span_all_columns && window->DC .CurrentColumns )
601+ PushColumnsBackground ();
602+ else if (span_all_columns && g.CurrentTable )
603+ TablePushBackgroundChannel ();
604+
585605 // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
586606 ImGuiButtonFlags button_flags = 0 ;
587607 if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
@@ -596,7 +616,7 @@ namespace IGFD
596616
597617 const bool was_selected = selected;
598618 bool hovered, held;
599- bool pressed = ButtonBehavior (bb_enlarged , id, &hovered, &held, button_flags);
619+ bool pressed = ButtonBehavior (bb , id, &hovered, &held, button_flags);
600620
601621 // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
602622 if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
@@ -618,20 +638,22 @@ namespace IGFD
618638 window->DC .LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
619639
620640 // Render
621- if (( held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld) ) || vFlashing)
641+ if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld) || vFlashing)
622642 hovered = true ;
623643 if (hovered || selected)
624644 {
625645 const ImU32 col = GetColorU32 ((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
626- RenderFrame (bb_enlarged .Min , bb_enlarged .Max , col, false , 0 .0f );
627- RenderNavHighlight (bb_enlarged , id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
646+ RenderFrame (bb .Min , bb .Max , col, false , 0 .0f );
647+ RenderNavHighlight (bb , id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
628648 }
629649
630- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC .CurrentColumns )
650+ if (span_all_columns && window->DC .CurrentColumns )
631651 PopColumnsBackground ();
652+ else if (span_all_columns && g.CurrentTable )
653+ TablePopBackgroundChannel ();
632654
633655 if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor (ImGuiCol_Text, style.Colors [ImGuiCol_TextDisabled]);
634- RenderTextClipped (text_min, text_max, label, NULL , &label_size, style.SelectableTextAlign , &bb_enlarged );
656+ RenderTextClipped (text_min, text_max, label, NULL , &label_size, style.SelectableTextAlign , &bb );
635657 if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor ();
636658
637659 // Automatically close popups
@@ -1294,12 +1316,13 @@ namespace IGFD
12941316 | ImGuiTableFlags_Sortable
12951317#endif // USE_CUSTOM_SORTING_ICON
12961318 ;
1297- if (ImGui::BeginTable (" ##fileTable" , 3 , flags, vSize))
1319+ if (ImGui::BeginTable (" ##fileTable" , 4 , flags, vSize))
12981320 {
12991321 ImGui::TableSetupScrollFreeze (0 , 1 ); // Make header always visible
13001322 ImGui::TableSetupColumn (m_HeaderFileName.c_str (), ImGuiTableColumnFlags_WidthStretch, -1 , 0 );
1301- ImGui::TableSetupColumn (m_HeaderFileSize.c_str (), ImGuiTableColumnFlags_WidthFixed, -1 , 1 );
1302- ImGui::TableSetupColumn (m_HeaderFileDate.c_str (), ImGuiTableColumnFlags_WidthFixed, -1 , 2 );
1323+ ImGui::TableSetupColumn (m_HeaderFileType.c_str (), ImGuiTableColumnFlags_WidthFixed, -1 , 1 );
1324+ ImGui::TableSetupColumn (m_HeaderFileSize.c_str (), ImGuiTableColumnFlags_WidthFixed, -1 , 2 );
1325+ ImGui::TableSetupColumn (m_HeaderFileDate.c_str (), ImGuiTableColumnFlags_WidthFixed, -1 , 3 );
13031326
13041327#ifndef USE_CUSTOM_SORTING_ICON
13051328 // Sort our data if sort specs have been changed!
@@ -1310,8 +1333,10 @@ namespace IGFD
13101333 if (sorts_specs->Specs ->ColumnUserID == 0 )
13111334 SortFields (SortingFieldEnum::FIELD_FILENAME, true );
13121335 else if (sorts_specs->Specs ->ColumnUserID == 1 )
1313- SortFields (SortingFieldEnum::FIELD_SIZE , true );
1336+ SortFields (SortingFieldEnum::FIELD_TYPE , true );
13141337 else if (sorts_specs->Specs ->ColumnUserID == 2 )
1338+ SortFields (SortingFieldEnum::FIELD_SIZE, true );
1339+ else // if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we add a fourth column
13151340 SortFields (SortingFieldEnum::FIELD_DATE, true );
13161341
13171342 sorts_specs->SpecsDirty = false ;
@@ -1321,7 +1346,7 @@ namespace IGFD
13211346 ImGui::TableHeadersRow ();
13221347#else // USE_CUSTOM_SORTING_ICON
13231348 ImGui::TableNextRow (ImGuiTableRowFlags_Headers);
1324- for (int column = 0 ; column < 3 ; column++)
1349+ for (int column = 0 ; column < 4 ; column++)
13251350 {
13261351 ImGui::TableSetColumnIndex (column);
13271352 const char * column_name = ImGui::TableGetColumnName (column); // Retrieve name passed to TableSetupColumn()
@@ -1333,8 +1358,10 @@ namespace IGFD
13331358 if (column == 0 )
13341359 SortFields (SortingFieldEnum::FIELD_FILENAME, true );
13351360 else if (column == 1 )
1361+ SortFields (SortingFieldEnum::FIELD_TYPE, true );
1362+ else if (column == 2 )
13361363 SortFields (SortingFieldEnum::FIELD_SIZE, true );
1337- else // if (column == 2 ) => alwasy true for the moment, to uncomment if we add a fourth column
1364+ else // if (column == 3 ) => alwayd true for the moment, to uncomment if we add a fourth column
13381365 SortFields (SortingFieldEnum::FIELD_DATE, true );
13391366 }
13401367 }
@@ -1367,28 +1394,33 @@ namespace IGFD
13671394 str = fileEntryString + str;
13681395 }
13691396 bool selected = (m_SelectedFileNames.find (infos.fileName ) != m_SelectedFileNames.end ()); // found
1397+
13701398 ImGui::TableNextRow ();
13711399
13721400 bool needToBreakTheloop = false ;
13731401
1374- if (ImGui::TableSetColumnIndex ( 0 )) // first column
1402+ if (ImGui::TableNextColumn ( )) // file name
13751403 {
13761404 needToBreakTheloop = SelectableItem (i, infos, selected, str.c_str ());
13771405 }
1378- if (ImGui::TableSetColumnIndex (1 )) // second column
1406+ if (ImGui::TableNextColumn ()) // file type
1407+ {
1408+ ImGui::Text (" %s" , infos.ext .c_str ());
1409+ }
1410+ if (ImGui::TableNextColumn ()) // file size
13791411 {
13801412 if (infos.type != ' d' )
13811413 {
1382- needToBreakTheloop = SelectableItem (i, infos, selected, " %s " , infos.formatedFileSize .c_str ());
1414+ ImGui::Text ( " %s " , infos.formatedFileSize .c_str ());
13831415 }
13841416 else
13851417 {
1386- needToBreakTheloop = SelectableItem (i, infos, selected, " " );
1418+ ImGui::Text ( " " );
13871419 }
13881420 }
1389- if (ImGui::TableSetColumnIndex ( 2 )) // third column
1421+ if (ImGui::TableNextColumn ( )) // file date + time
13901422 {
1391- needToBreakTheloop = SelectableItem (i, infos, selected, " %s" , infos.fileModifDate .c_str ());
1423+ ImGui::Text ( " %s" , infos.fileModifDate .c_str ());
13921424 }
13931425
13941426 if (showColor)
@@ -1954,6 +1986,7 @@ namespace IGFD
19541986 if (vSortingField != SortingFieldEnum::FIELD_NONE)
19551987 {
19561988 m_HeaderFileName = tableHeaderFileNameString;
1989+ m_HeaderFileType = tableHeaderFileTypeString;
19571990 m_HeaderFileSize = tableHeaderFileSizeString;
19581991 m_HeaderFileDate = tableHeaderFileDateString;
19591992 }
@@ -1988,13 +2021,43 @@ namespace IGFD
19882021 });
19892022 }
19902023 }
1991- else if (vSortingField == SortingFieldEnum::FIELD_SIZE )
2024+ else if (vSortingField == SortingFieldEnum::FIELD_TYPE )
19922025 {
19932026 if (vCanChangeOrder && m_SortingField == vSortingField)
19942027 m_SortingDirection[1 ] = !m_SortingDirection[1 ];
19952028
19962029 if (m_SortingDirection[1 ])
19972030 {
2031+ #ifdef USE_CUSTOM_SORTING_ICON
2032+ m_HeaderFileType = tableHeaderDescendingIcon + m_HeaderFileType;
2033+ #endif // USE_CUSTOM_SORTING_ICON
2034+ std::sort (m_FileList.begin (), m_FileList.end (),
2035+ [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool
2036+ {
2037+ if (a.type != b.type ) return (a.type == ' d' ); // directory in first
2038+ return (a.ext < b.ext ); // else
2039+ });
2040+ }
2041+ else
2042+ {
2043+ #ifdef USE_CUSTOM_SORTING_ICON
2044+ m_HeaderFileType = tableHeaderAscendingIcon + m_HeaderFileType;
2045+ #endif // USE_CUSTOM_SORTING_ICON
2046+ std::sort (m_FileList.begin (), m_FileList.end (),
2047+ [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool
2048+ {
2049+ if (a.type != b.type ) return (a.type != ' d' ); // directory in last
2050+ return (a.ext > b.ext ); // else
2051+ });
2052+ }
2053+ }
2054+ else if (vSortingField == SortingFieldEnum::FIELD_SIZE)
2055+ {
2056+ if (vCanChangeOrder && m_SortingField == vSortingField)
2057+ m_SortingDirection[2 ] = !m_SortingDirection[2 ];
2058+
2059+ if (m_SortingDirection[2 ])
2060+ {
19982061#ifdef USE_CUSTOM_SORTING_ICON
19992062 m_HeaderFileSize = tableHeaderDescendingIcon + m_HeaderFileSize;
20002063#endif // USE_CUSTOM_SORTING_ICON
@@ -2021,9 +2084,9 @@ namespace IGFD
20212084 else if (vSortingField == SortingFieldEnum::FIELD_DATE)
20222085 {
20232086 if (vCanChangeOrder && m_SortingField == vSortingField)
2024- m_SortingDirection[2 ] = !m_SortingDirection[2 ];
2087+ m_SortingDirection[3 ] = !m_SortingDirection[3 ];
20252088
2026- if (m_SortingDirection[2 ])
2089+ if (m_SortingDirection[3 ])
20272090 {
20282091#ifdef USE_CUSTOM_SORTING_ICON
20292092 m_HeaderFileDate = tableHeaderDescendingIcon + m_HeaderFileDate;
0 commit comments