Skip to content

Commit 81d9023

Browse files
v-einhoffstadt
authored andcommitted
feat (mvListbox): Enabled scrolling on listbox widgets (#2298)
1 parent be049e2 commit 81d9023

4 files changed

Lines changed: 65 additions & 12 deletions

File tree

dearpygui/type_info.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1273,7 +1273,7 @@ def get_applicable_states(**kwargs) -> dict:
12731273
"mvAppItemType::mvCollapsingHeader": ("ok", "pos", "hovered", "active", "focused", "clicked", "left_clicked", "right_clicked", "middle_clicked", "visible", "activated", "deactivated", "toggled_open", "rect_min", "rect_max", "rect_size", "resized", "content_region_avail", ),
12741274
"mvAppItemType::mvSeparator": ("ok", "pos", ),
12751275
"mvAppItemType::mvCheckbox": ("ok", "pos", "hovered", "active", "focused", "clicked", "left_clicked", "right_clicked", "middle_clicked", "visible", "activated", "deactivated", "deactivated_after_edit", "rect_min", "rect_max", "rect_size", "resized", "content_region_avail", ),
1276-
"mvAppItemType::mvListbox": ("ok", "pos", "hovered", "active", "focused", "clicked", "left_clicked", "right_clicked", "middle_clicked", "visible", "edited", "activated", "deactivated", "deactivated_after_edit", "rect_min", "rect_max", "rect_size", "resized", "content_region_avail", ),
1276+
"mvAppItemType::mvListbox": ("ok", "pos", "hovered", "active", "focused", "clicked", "left_clicked", "right_clicked", "middle_clicked", "visible", "edited", "activated", "deactivated", "deactivated_after_edit", "rect_min", "rect_max", "rect_size", "resized", "content_region_avail", "scrolled", "is_scrolling", "scroll_pos", "scroll_max", ),
12771277
"mvAppItemType::mvText": ("ok", "pos", "hovered", "clicked", "left_clicked", "right_clicked", "middle_clicked", "visible", "rect_min", "rect_max", "rect_size", "resized", "content_region_avail", ),
12781278
"mvAppItemType::mvCombo": ("ok", "pos", "hovered", "active", "focused", "clicked", "left_clicked", "right_clicked", "middle_clicked", "visible", "edited", "activated", "deactivated", "deactivated_after_edit", "rect_min", "rect_max", "rect_size", "resized", "content_region_avail", ),
12791279
"mvAppItemType::mvPlot": ("ok", "pos", "hovered", "active", "focused", "clicked", "left_clicked", "right_clicked", "middle_clicked", "visible", "activated", "deactivated", "rect_min", "rect_max", "rect_size", "resized", "content_region_avail", ),

src/dearpygui_commands.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ set_x_scroll(PyObject* self, PyObject* args, PyObject* kwargs)
233233
else
234234
{
235235
mvThrowPythonError(mvErrorCode::mvIncompatibleType, "set_x_scroll",
236-
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable", window);
236+
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable, mvListbox", window);
237237
return nullptr;
238238
}
239239

@@ -272,7 +272,7 @@ set_y_scroll(PyObject* self, PyObject* args, PyObject* kwargs)
272272
else
273273
{
274274
mvThrowPythonError(mvErrorCode::mvIncompatibleType, "set_y_scroll",
275-
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable", window);
275+
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable, mvListbox", window);
276276
return nullptr;
277277
}
278278

@@ -308,7 +308,7 @@ get_x_scroll(PyObject* self, PyObject* args, PyObject* kwargs)
308308
else
309309
{
310310
mvThrowPythonError(mvErrorCode::mvIncompatibleType, "get_x_scroll",
311-
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable", window);
311+
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable, mvListbox", window);
312312
return nullptr;
313313
}
314314

@@ -344,7 +344,7 @@ get_y_scroll(PyObject* self, PyObject* args, PyObject* kwargs)
344344
else
345345
{
346346
mvThrowPythonError(mvErrorCode::mvIncompatibleType, "get_y_scroll",
347-
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable", window);
347+
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable, mvListbox", window);
348348
return nullptr;
349349
}
350350

@@ -380,7 +380,7 @@ get_x_scroll_max(PyObject* self, PyObject* args, PyObject* kwargs)
380380
else
381381
{
382382
mvThrowPythonError(mvErrorCode::mvIncompatibleType, "get_x_scroll_max",
383-
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable", window);
383+
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable, mvListbox", window);
384384
return nullptr;
385385
}
386386

@@ -416,7 +416,7 @@ get_y_scroll_max(PyObject* self, PyObject* args, PyObject* kwargs)
416416
else
417417
{
418418
mvThrowPythonError(mvErrorCode::mvIncompatibleType, "get_y_scroll_max",
419-
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable", window);
419+
"Incompatible type. Expected types include: mvWindowAppItem, mvChildWindow, mvTable, mvListbox", window);
420420
return nullptr;
421421
}
422422

src/mvAppItem.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,8 @@ CanItemTypeBeScrolled(mvAppItemType type)
954954
{
955955
case mvAppItemType::mvWindowAppItem:
956956
case mvAppItemType::mvChildWindow:
957-
case mvAppItemType::mvTable: return true;
957+
case mvAppItemType::mvTable:
958+
case mvAppItemType::mvListbox: return true;
958959
default: return false;
959960
}
960961

src/mvBasicWidgets.cpp

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3976,15 +3976,67 @@ DearPyGui::draw_listbox(ImDrawList *drawlist, mvAppItem &item, mvListboxConfig &
39763976
config.disabledindex = config.index;
39773977
}
39783978

3979+
item.handleImmediateScroll();
3980+
39793981
// remap Header to FrameBgActive
39803982
ImGuiStyle* style = &ImGui::GetStyle();
39813983
ImGui::PushStyleColor(ImGuiCol_Header, style->Colors[ImGuiCol_FrameBgActive]);
39823984

3983-
if (ImGui::ListBox(item.info.internalLabel.c_str(), item.config.enabled ? &config.index : &config.disabledindex, config.charNames.data(), (int)config.names.size(), config.itemsHeight))
3985+
// We cannot use `ImGui::ListBox()` directly because to apply scrolling,
3986+
// we need access to the child window and `ListBox()` does not expose it.
3987+
// That's why we have to reimplement it using BeginListBox/EndListBox
3988+
// (as ImGui recommends it anyway). Ideally, the code below needs to be
3989+
// kept in sync, more or less, with `ImGui::ListBox()`.
3990+
int items_count = (int)config.names.size();
3991+
int* current_item = item.config.enabled ? &config.index : &config.disabledindex;
3992+
3993+
int height_in_items = config.itemsHeight;
3994+
if (height_in_items < 0)
3995+
height_in_items = ImMin(items_count, 7);
3996+
float height_in_items_f = (float)height_in_items + 0.25f;
3997+
ImVec2 size(0.0f, ImTrunc(ImGui::GetTextLineHeightWithSpacing() * height_in_items_f + ImGui::GetStyle().FramePadding.y * 2.0f));
3998+
3999+
if (ImGui::BeginListBox(item.info.internalLabel.c_str(), size))
39844000
{
3985-
*config.value = config.names[config.index];
3986-
config.disabled_value = config.names[config.index];
3987-
item.submitCallback(*config.value);
4001+
bool value_changed = false;
4002+
ImGuiListClipper clipper;
4003+
clipper.Begin(items_count, ImGui::GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
4004+
clipper.IncludeItemByIndex(*current_item);
4005+
while (clipper.Step())
4006+
{
4007+
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
4008+
{
4009+
ImGui::PushID(i);
4010+
const bool item_selected = (i == *current_item);
4011+
if (ImGui::Selectable(config.charNames[i], item_selected))
4012+
{
4013+
*current_item = i;
4014+
value_changed = true;
4015+
}
4016+
if (item_selected)
4017+
ImGui::SetItemDefaultFocus();
4018+
ImGui::PopID();
4019+
}
4020+
}
4021+
4022+
item.handleDelayedScroll();
4023+
item.config.scrollXFlags = item.config.scrollYFlags = mvSetScrollFlags_None;
4024+
UpdateAppItemScrollInfo(item.state);
4025+
4026+
ImGui::EndListBox();
4027+
4028+
if (value_changed)
4029+
{
4030+
ImGuiContext& g = *GImGui;
4031+
ImGui::MarkItemEdited(g.LastItemData.ID);
4032+
4033+
*config.value = config.names[config.index];
4034+
// TODO: we need to review how listbox behaves in the disabled state. It does
4035+
// not look good that it uses `index` rather than `disabledindex` here. Neither
4036+
// does `config.value` in `submitCallback` below.
4037+
config.disabled_value = config.names[config.index];
4038+
item.submitCallback(*config.value);
4039+
}
39884040
}
39894041

39904042
ImGui::PopStyleColor();

0 commit comments

Comments
 (0)