Skip to content

Commit cc23d00

Browse files
v-einhoffstadt
authored andcommitted
feat (handlers): Focus and hover handlers can now track state changes like "mouse-in" or "lost focus".
1 parent 2362735 commit cc23d00

13 files changed

+132
-41
lines changed

dearpygui/_dearpygui.pyi

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dearpygui/_dearpygui_RTD.py

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dearpygui/dearpygui.py

Lines changed: 10 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dearpygui.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ GetModuleConstants()
6969
ModuleConstants.push_back({"mvComboHeight_Large", 2L });
7070
ModuleConstants.push_back({"mvComboHeight_Largest", 3L });
7171

72+
ModuleConstants.push_back({"mvEventType_Off", mvEventType_Off });
73+
ModuleConstants.push_back({"mvEventType_Enter", mvEventType_Enter });
74+
ModuleConstants.push_back({"mvEventType_On", mvEventType_On });
75+
ModuleConstants.push_back({"mvEventType_Leave", mvEventType_Leave });
76+
7277
ModuleConstants.push_back({"mvPlatform_Windows", 0L });
7378
ModuleConstants.push_back({"mvPlatform_Apple", 1L });
7479
ModuleConstants.push_back({"mvPlatform_Linux", 2L });

src/mvAppItem.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4855,6 +4855,8 @@ DearPyGui::GetEntityParser(mvAppItemType type)
48554855
MV_PARSER_ARG_CALLBACK)
48564856
);
48574857

4858+
args.push_back({ mvPyDataType::Integer, "event_type", mvArgType::KEYWORD_ARG, "None", "What kind of events to track: mouse-in (mvEventType_Enter), mouse-over (mvEventType_On), mouse-out (mvEventType_Leave). Can be a combination of these flags. Defaults to mouse-over." });
4859+
48584860
setup.about = "Adds a hover handler.";
48594861
setup.category = { "Widgets", "Events" };
48604862
break;
@@ -4881,6 +4883,8 @@ DearPyGui::GetEntityParser(mvAppItemType type)
48814883
MV_PARSER_ARG_CALLBACK)
48824884
);
48834885

4886+
args.push_back({ mvPyDataType::Integer, "event_type", mvArgType::KEYWORD_ARG, "None", "What kind of events to track: just got focus (mvEventType_Enter), currently having focus (mvEventType_On), lost focus (mvEventType_Leave). Can be a combination of these flags. Defaults to mvEventType_On." });
4887+
48844888
setup.about = "Adds a focus handler.";
48854889
setup.category = { "Widgets", "Events" };
48864890
break;

src/mvAppItemState.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ void
88
ResetAppItemState(mvAppItemState& state)
99
{
1010
state.hovered = false;
11+
state.prevHovered = false;
1112
state.active = false;
1213
state.focused = false;
14+
state.prevFocused = false;
1315
state.leftclicked = false;
1416
state.rightclicked = false;
1517
state.middleclicked = false;
@@ -27,8 +29,10 @@ void
2729
UpdateAppItemState(mvAppItemState& state)
2830
{
2931
state.lastFrameUpdate = GContext->frame;
32+
state.prevHovered = state.hovered;
3033
state.hovered = ImGui::IsItemHovered();
3134
state.active = ImGui::IsItemActive();
35+
state.prevFocused = state.focused;
3236
state.focused = ImGui::IsItemFocused();
3337
if (state.focused)
3438
{

src/mvAppItemState.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ inline b8 IsItemDoubleClicked(ImGuiMouseButton mouse_button)
6666
struct mvAppItemState
6767
{
6868
b8 hovered = false;
69+
b8 prevHovered = false;
6970
b8 active = false;
7071
b8 focused = false;
72+
b8 prevFocused = false;
7173
b8 leftclicked = false;
7274
b8 rightclicked = false;
7375
b8 middleclicked = false;

src/mvBasicWidgets.cpp

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2642,6 +2642,7 @@ DearPyGui::draw_simple_plot(ImDrawList* drawlist, mvAppItem& item, const mvSimpl
26422642
// * only update if applicable
26432643
//-----------------------------------------------------------------------------
26442644
item.state.lastFrameUpdate = GContext->frame;
2645+
item.state.prevHovered = item.state.hovered;
26452646
item.state.hovered = ImGui::IsItemHovered();
26462647
item.state.leftclicked = ImGui::IsItemClicked();
26472648
item.state.rightclicked = ImGui::IsItemClicked(1);
@@ -4388,26 +4389,7 @@ DearPyGui::draw_radio_button(ImDrawList* drawlist, mvAppItem& item, mvRadioButto
43884389
//-----------------------------------------------------------------------------
43894390
// update state
43904391
//-----------------------------------------------------------------------------
4391-
item.state.lastFrameUpdate = GContext->frame;
4392-
item.state.hovered = ImGui::IsItemHovered();
4393-
item.state.active = ImGui::IsItemActive();
4394-
item.state.focused = ImGui::IsItemFocused();
4395-
item.state.leftclicked = ImGui::IsItemClicked();
4396-
item.state.rightclicked = ImGui::IsItemClicked(1);
4397-
item.state.middleclicked = ImGui::IsItemClicked(2);
4398-
for (int i = 0; i < item.state.doubleclicked.size(); i++)
4399-
{
4400-
item.state.doubleclicked[i] = IsItemDoubleClicked(i);
4401-
}
4402-
item.state.visible = ImGui::IsItemVisible();
4403-
item.state.activated = ImGui::IsItemActivated();
4404-
item.state.deactivated = ImGui::IsItemDeactivated();
4405-
item.state.deactivatedAfterEdit = ImGui::IsItemDeactivatedAfterEdit();
4406-
item.state.toggledOpen = ImGui::IsItemToggledOpen();
4407-
item.state.rectMin = { ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y };
4408-
item.state.rectMax = { ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y };
4409-
item.state.rectSize = { ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y };
4410-
item.state.contextRegionAvail = { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y };
4392+
UpdateAppItemState(item.state);
44114393

44124394
//-----------------------------------------------------------------------------
44134395
// post draw

src/mvContainers.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,9 @@ DearPyGui::draw_menu(ImDrawList* drawlist, mvAppItem& item, mvMenuConfig& config
639639
item.state.active = ImGui::IsItemActive();
640640
item.state.activated = ImGui::IsItemActivated();
641641
item.state.deactivated = ImGui::IsItemDeactivated();
642+
item.state.prevFocused = item.state.focused;
642643
item.state.focused = ImGui::IsWindowFocused();
644+
item.state.prevHovered = item.state.hovered;
643645
item.state.hovered = ImGui::IsWindowHovered();
644646
item.state.rectSize = { ImGui::GetWindowWidth(), ImGui::GetWindowHeight() };
645647
item.state.contextRegionAvail = { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y };
@@ -682,7 +684,9 @@ DearPyGui::draw_menu(ImDrawList* drawlist, mvAppItem& item, mvMenuConfig& config
682684
item.state.active = ImGui::IsItemActive();
683685
item.state.activated = ImGui::IsItemActivated();
684686
item.state.deactivated = ImGui::IsItemDeactivated();
687+
item.state.prevFocused = item.state.focused;
685688
item.state.focused = false;
689+
item.state.prevHovered = item.state.hovered;
686690
item.state.hovered = false;
687691
item.state.rectSize = { 0.0f, 0.0f };
688692
item.state.contextRegionAvail = { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y };
@@ -779,6 +783,7 @@ DearPyGui::draw_tab(ImDrawList* drawlist, mvAppItem& item, mvTabConfig& config)
779783
}
780784

781785
item.state.lastFrameUpdate = GContext->frame;
786+
item.state.prevHovered = item.state.hovered;
782787
// create tab item and see if it is selected
783788
if (ImGui::BeginTabItem(item.info.internalLabel.c_str(), config.closable ? &item.config.show : nullptr, config._flags))
784789
{
@@ -927,7 +932,9 @@ DearPyGui::draw_child_window(ImDrawList* drawlist, mvAppItem& item, mvChildWindo
927932
item.state.lastFrameUpdate = GContext->frame;
928933
item.state.active = ImGui::IsItemActive();
929934
item.state.deactivated = ImGui::IsItemDeactivated();
935+
item.state.prevFocused = item.state.focused;
930936
item.state.focused = ImGui::IsWindowFocused();
937+
item.state.prevHovered = item.state.hovered;
931938
item.state.hovered = ImGui::IsWindowHovered();
932939
item.state.rectSize = { ImGui::GetWindowWidth(), ImGui::GetWindowHeight() };
933940
item.state.contextRegionAvail = { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y };
@@ -1511,7 +1518,9 @@ DearPyGui::draw_window(ImDrawList* drawlist, mvAppItem& item, mvWindowAppItemCon
15111518
// shouldn't have to do this but do. Fix later
15121519
item.config.show = false;
15131520
item.state.lastFrameUpdate = GContext->frame;
1521+
item.state.prevHovered = item.state.hovered;
15141522
item.state.hovered = false;
1523+
item.state.prevFocused = item.state.focused;
15151524
item.state.focused = false;
15161525
item.state.toggledOpen = false;
15171526
item.state.visible = false;
@@ -1550,7 +1559,9 @@ DearPyGui::draw_window(ImDrawList* drawlist, mvAppItem& item, mvWindowAppItemCon
15501559
item.config.show = false;
15511560
// Update item state so that get_item_state is valid
15521561
item.state.lastFrameUpdate = GContext->frame;
1562+
item.state.prevHovered = item.state.hovered;
15531563
item.state.hovered = false;
1564+
item.state.prevFocused = item.state.focused;
15541565
item.state.focused = false;
15551566
item.state.toggledOpen = false;
15561567
item.state.visible = false;
@@ -1658,7 +1669,9 @@ DearPyGui::draw_window(ImDrawList* drawlist, mvAppItem& item, mvWindowAppItemCon
16581669

16591670
item.state.lastFrameUpdate = GContext->frame;
16601671
item.state.visible = true;
1672+
item.state.prevHovered = item.state.hovered;
16611673
item.state.hovered = ImGui::IsWindowHovered();
1674+
item.state.prevFocused = item.state.focused;
16621675
item.state.focused = ImGui::IsWindowFocused();
16631676
item.state.rectSize = { ImGui::GetWindowSize().x, ImGui::GetWindowSize().y };
16641677
item.state.toggledOpen = ImGui::IsWindowCollapsed();
@@ -1705,7 +1718,9 @@ DearPyGui::draw_window(ImDrawList* drawlist, mvAppItem& item, mvWindowAppItemCon
17051718
if (!item.config.show)
17061719
{
17071720
item.state.lastFrameUpdate = GContext->frame;
1721+
item.state.prevHovered = item.state.hovered;
17081722
item.state.hovered = false;
1723+
item.state.prevFocused = item.state.focused;
17091724
item.state.focused = false;
17101725
item.state.toggledOpen = false;
17111726
item.state.visible = false;

src/mvItemHandlers.cpp

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,40 @@ void mvItemHandler::submitHandler(mvAppItem* parent)
131131
});
132132
}
133133

134+
void mvBoolStateHandler::checkEvent(bool curState, bool prevState, mvAppItem* parent)
135+
{
136+
mvEventType eventType = curState?
137+
(prevState? mvEventType_On : mvEventType_EnterAndOn) :
138+
(prevState? mvEventType_LeaveAndOff : mvEventType_Off);
139+
140+
if (trackedEventType & eventType)
141+
{
142+
// We do not pass eventType to callback yet in order to keep it compatible
143+
// with the old version.
144+
submitHandler(parent);
145+
}
146+
}
147+
148+
void mvBoolStateHandler::handleSpecificKeywordArgs(PyObject* dict)
149+
{
150+
if (dict == nullptr)
151+
return;
152+
153+
if (PyObject* item = PyDict_GetItemString(dict, "event_type"))
154+
{
155+
if (item != Py_None)
156+
trackedEventType = static_cast<mvEventType>(ToInt(item));
157+
}
158+
}
159+
160+
void mvBoolStateHandler::getSpecificConfiguration(PyObject* dict)
161+
{
162+
if (dict == nullptr)
163+
return;
164+
165+
PyDict_SetItemString(dict, "event_type", mvPyObject(ToPyInt(trackedEventType)));
166+
}
167+
134168
void mvActivatedHandler::customAction(void* data)
135169
{
136170

@@ -277,21 +311,14 @@ void mvEditedHandler::customAction(void* data)
277311

278312
void mvFocusHandler::customAction(void* data)
279313
{
280-
281314
mvAppItemState* state = static_cast<mvAppItemState*>(data);
282-
if (state->focused)
283-
{
284-
submitHandler(state->parent);
285-
}
315+
checkEvent(state->focused, state->prevFocused, state->parent);
286316
}
287317

288318
void mvHoverHandler::customAction(void* data)
289319
{
290320
mvAppItemState* state = static_cast<mvAppItemState*>(data);
291-
if (state->hovered)
292-
{
293-
submitHandler(state->parent);
294-
}
321+
checkEvent(state->hovered, state->prevHovered, state->parent);
295322
}
296323

297324
void mvResizeHandler::customAction(void* data)

0 commit comments

Comments
 (0)