diff --git a/dearpygui/dearpygui.py b/dearpygui/dearpygui.py index d8c0561d4..d3ae9b43e 100644 --- a/dearpygui/dearpygui.py +++ b/dearpygui/dearpygui.py @@ -1485,7 +1485,9 @@ def set_start_callback(callback): @contextmanager -def child_window(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, height: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', drop_callback: Callable =None, show: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', delay_search: bool =False, tracked: bool =False, track_offset: float =0.5, border: bool =True, autosize_x: bool =False, autosize_y: bool =False, no_scrollbar: bool =False, horizontal_scrollbar: bool =False, menubar: bool =False, no_scroll_with_mouse: bool =False, **kwargs) -> Union[int, str]: + +def child_window(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, height: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', drop_callback: Callable =None, show: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', delay_search: bool =False, tracked: bool =False, track_offset: float =0.5, border: bool =True, autosize_x: bool =False, autosize_y: bool =False, no_scrollbar: bool =False, horizontal_scrollbar: bool =False, menubar: bool =False, no_scroll_with_mouse: bool=False, flattened_navigation: bool=True, **kwargs) -> Union[int, str]: + """ Adds an embedded child window. Will show scrollbars when items do not fit. Args: @@ -1513,6 +1515,7 @@ def child_window(*, label: str =None, user_data: Any =None, use_internal_label: horizontal_scrollbar (bool, optional): Allow horizontal scrollbar to appear (off by default). menubar (bool, optional): Shows/Hides the menubar at the top. no_scroll_with_mouse (bool, optional): Disable user vertically scrolling with mouse wheel. + flattened_navigation (bool, optional): Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) id (Union[int, str], optional): (deprecated) Yields: Union[int, str] @@ -1522,7 +1525,7 @@ def child_window(*, label: str =None, user_data: Any =None, use_internal_label: if 'id' in kwargs.keys(): warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - widget = internal_dpg.add_child_window(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, height=height, indent=indent, parent=parent, before=before, payload_type=payload_type, drop_callback=drop_callback, show=show, pos=pos, filter_key=filter_key, delay_search=delay_search, tracked=tracked, track_offset=track_offset, border=border, autosize_x=autosize_x, autosize_y=autosize_y, no_scrollbar=no_scrollbar, horizontal_scrollbar=horizontal_scrollbar, menubar=menubar, no_scroll_with_mouse=no_scroll_with_mouse, **kwargs) + widget = internal_dpg.add_child_window(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, height=height, indent=indent, parent=parent, before=before, payload_type=payload_type, drop_callback=drop_callback, show=show, pos=pos, filter_key=filter_key, delay_search=delay_search, tracked=tracked, track_offset=track_offset, border=border, autosize_x=autosize_x, autosize_y=autosize_y, no_scrollbar=no_scrollbar, horizontal_scrollbar=horizontal_scrollbar, menubar=menubar, no_scroll_with_mouse=no_scroll_with_mouse, flattened_navigation=flattened_navigation, **kwargs) internal_dpg.push_container_stack(widget) yield widget finally: @@ -3136,7 +3139,7 @@ def add_checkbox(*, label: str =None, user_data: Any =None, use_internal_label: return internal_dpg.add_checkbox(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, indent=indent, parent=parent, before=before, source=source, payload_type=payload_type, callback=callback, drag_callback=drag_callback, drop_callback=drop_callback, show=show, enabled=enabled, pos=pos, filter_key=filter_key, tracked=tracked, track_offset=track_offset, default_value=default_value, **kwargs) -def add_child_window(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, height: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', drop_callback: Callable =None, show: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', delay_search: bool =False, tracked: bool =False, track_offset: float =0.5, border: bool =True, autosize_x: bool =False, autosize_y: bool =False, no_scrollbar: bool =False, horizontal_scrollbar: bool =False, menubar: bool =False, no_scroll_with_mouse: bool =False, **kwargs) -> Union[int, str]: +def add_child_window(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, height: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', drop_callback: Callable =None, show: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', delay_search: bool =False, tracked: bool =False, track_offset: float =0.5, border: bool =True, autosize_x: bool =False, autosize_y: bool =False, no_scrollbar: bool =False, horizontal_scrollbar: bool =False, menubar: bool =False, no_scroll_with_mouse: bool =False, flattened_navigation: bool=True, **kwargs) -> Union[int, str]: """ Adds an embedded child window. Will show scrollbars when items do not fit. Args: @@ -3164,6 +3167,7 @@ def add_child_window(*, label: str =None, user_data: Any =None, use_internal_lab horizontal_scrollbar (bool, optional): Allow horizontal scrollbar to appear (off by default). menubar (bool, optional): Shows/Hides the menubar at the top. no_scroll_with_mouse (bool, optional): Disable user vertically scrolling with mouse wheel. + flattened_navigation (bool, optional): Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) id (Union[int, str], optional): (deprecated) Returns: Union[int, str] @@ -3173,7 +3177,7 @@ def add_child_window(*, label: str =None, user_data: Any =None, use_internal_lab warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) tag=kwargs['id'] - return internal_dpg.add_child_window(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, height=height, indent=indent, parent=parent, before=before, payload_type=payload_type, drop_callback=drop_callback, show=show, pos=pos, filter_key=filter_key, delay_search=delay_search, tracked=tracked, track_offset=track_offset, border=border, autosize_x=autosize_x, autosize_y=autosize_y, no_scrollbar=no_scrollbar, horizontal_scrollbar=horizontal_scrollbar, menubar=menubar, no_scroll_with_mouse=no_scroll_with_mouse, **kwargs) + return internal_dpg.add_child_window(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, height=height, indent=indent, parent=parent, before=before, payload_type=payload_type, drop_callback=drop_callback, show=show, pos=pos, filter_key=filter_key, delay_search=delay_search, tracked=tracked, track_offset=track_offset, border=border, autosize_x=autosize_x, autosize_y=autosize_y, no_scrollbar=no_scrollbar, horizontal_scrollbar=horizontal_scrollbar, menubar=menubar, flattened_navigation=flattened_navigation, **kwargs) def add_clipper(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, width: int =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, show: bool =True, delay_search: bool =False, **kwargs) -> Union[int, str]: """ Helper to manually clip large list of items. Increases performance by not searching or drawing widgets outside of the clipped region. diff --git a/src/dearpygui_commands.h b/src/dearpygui_commands.h index 0e521270b..46c8e33ae 100644 --- a/src/dearpygui_commands.h +++ b/src/dearpygui_commands.h @@ -2629,6 +2629,8 @@ configure_app(PyObject* self, PyObject* args, PyObject* kwargs) if (PyObject* item = PyDict_GetItemString(kwargs, "device_name")) GContext->IO.info_device_name = ToString(item); if (PyObject* item = PyDict_GetItemString(kwargs, "device")) GContext->IO.info_device = ToInt(item); + if (PyObject* item = PyDict_GetItemString(kwargs, "keyboard_navigation")) GContext->IO.kbdNavigation = ToBool(item); + return GetPyNone(); } @@ -2656,6 +2658,7 @@ get_app_configuration(PyObject* self, PyObject* args, PyObject* kwargs) PyDict_SetItemString(pdict, "auto_save_init_file", mvPyObject(ToPyBool(GContext->IO.autoSaveIniFile))); PyDict_SetItemString(pdict, "wait_for_input", mvPyObject(ToPyBool(GContext->IO.waitForInput))); PyDict_SetItemString(pdict, "manual_callback_management", mvPyObject(ToPyBool(GContext->IO.manualCallbacks))); + PyDict_SetItemString(pdict, "keyboard_navigation", mvPyObject(ToPyBool(GContext->IO.kbdNavigation))); return pdict; } diff --git a/src/dearpygui_parsers.h b/src/dearpygui_parsers.h index c11a1f437..f86320c12 100644 --- a/src/dearpygui_parsers.h +++ b/src/dearpygui_parsers.h @@ -496,6 +496,7 @@ InsertParser_Block1(std::map& parsers) args.push_back({ mvPyDataType::Bool, "skip_keyword_args", mvArgType::KEYWORD_ARG, "False" }); args.push_back({ mvPyDataType::Bool, "wait_for_input", mvArgType::KEYWORD_ARG, "False", "New in 1.1. Only update when user input occurs" }); args.push_back({ mvPyDataType::Bool, "manual_callback_management", mvArgType::KEYWORD_ARG, "False", "New in 1.2"}); + args.push_back({ mvPyDataType::Bool, "keyboard_navigation", mvArgType::KEYWORD_ARG, "False", "Keyboard navigation using arrow keys" }); mvPythonParserSetup setup; setup.about = "Configures app."; diff --git a/src/mvAppItem.cpp b/src/mvAppItem.cpp index 0aab5f96e..d9d333e28 100644 --- a/src/mvAppItem.cpp +++ b/src/mvAppItem.cpp @@ -2083,6 +2083,7 @@ DearPyGui::GetEntityParser(mvAppItemType type) args.push_back({ mvPyDataType::Bool, "horizontal_scrollbar", mvArgType::KEYWORD_ARG, "False", "Allow horizontal scrollbar to appear (off by default)." }); args.push_back({ mvPyDataType::Bool, "menubar", mvArgType::KEYWORD_ARG, "False", "Shows/Hides the menubar at the top." }); args.push_back({ mvPyDataType::Bool, "no_scroll_with_mouse", mvArgType::KEYWORD_ARG, "False", "Disable user vertically scrolling with mouse wheel." }); + args.push_back({ mvPyDataType::Bool, "flattened_navigation", mvArgType::KEYWORD_ARG, "True", "Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!)" }); setup.about = "Adds an embedded child window. Will show scrollbars when items do not fit."; setup.category = { "Containers", "Widgets" }; diff --git a/src/mvContainers.cpp b/src/mvContainers.cpp index 7e21130a8..a24421c06 100644 --- a/src/mvContainers.cpp +++ b/src/mvContainers.cpp @@ -69,6 +69,7 @@ DearPyGui::fill_configuration_dict(const mvChildWindowConfig& inConfig, PyObject checkbitset("horizontal_scrollbar", ImGuiWindowFlags_HorizontalScrollbar, inConfig.windowflags); checkbitset("menubar", ImGuiWindowFlags_MenuBar, inConfig.windowflags); checkbitset("no_scroll_with_mouse", ImGuiWindowFlags_NoScrollWithMouse, inConfig.windowflags); + checkbitset("flattened_navigation", ImGuiWindowFlags_NavFlattened, inConfig.windowflags); } void @@ -274,6 +275,7 @@ DearPyGui::set_configuration(PyObject* inDict, mvChildWindowConfig& outConfig) flagop("horizontal_scrollbar", ImGuiWindowFlags_HorizontalScrollbar, outConfig.windowflags); flagop("menubar", ImGuiWindowFlags_MenuBar, outConfig.windowflags); flagop("no_scroll_with_mouse", ImGuiWindowFlags_NoScrollWithMouse, outConfig.windowflags); + flagop("flattened_navigation", ImGuiWindowFlags_NavFlattened, outConfig.windowflags); } diff --git a/src/mvContainers.h b/src/mvContainers.h index d5a3cf7a8..7e57ecc4e 100644 --- a/src/mvContainers.h +++ b/src/mvContainers.h @@ -91,7 +91,7 @@ struct mvChildWindowConfig bool border = true; bool autosize_x = false; bool autosize_y = false; - ImGuiWindowFlags windowflags = ImGuiWindowFlags_NoSavedSettings; + ImGuiWindowFlags windowflags = ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NavFlattened; float scrollX = 0.0f; float scrollY = 0.0f; float scrollMaxX = 0.0f; diff --git a/src/mvContext.h b/src/mvContext.h index 8da80f638..1a9022110 100644 --- a/src/mvContext.h +++ b/src/mvContext.h @@ -78,6 +78,8 @@ struct mvIO bool docking = false; bool dockingViewport = false; + bool kbdNavigation = false; + std::string iniFile; bool loadIniFile = false; bool autoSaveIniFile = false; diff --git a/src/mvViewport_apple.mm b/src/mvViewport_apple.mm index 3738c8be7..c9aade816 100644 --- a/src/mvViewport_apple.mm +++ b/src/mvViewport_apple.mm @@ -116,7 +116,10 @@ } (void) io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + + if(GContext->IO.kbdNavigation) + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls if(GContext->IO.docking) diff --git a/src/mvViewport_linux.cpp b/src/mvViewport_linux.cpp index 892699034..26a1407e6 100644 --- a/src/mvViewport_linux.cpp +++ b/src/mvViewport_linux.cpp @@ -212,6 +212,9 @@ mvShowViewport(mvViewport& viewport, bool minimized, bool maximized) io.IniFilename = GContext->IO.iniFile.c_str(); } + if(GContext->IO.kbdNavigation) + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + if (GContext->IO.docking) io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; diff --git a/src/mvViewport_win32.cpp b/src/mvViewport_win32.cpp index 4cf006552..3ba1f0f61 100644 --- a/src/mvViewport_win32.cpp +++ b/src/mvViewport_win32.cpp @@ -420,6 +420,9 @@ mvShowViewport(mvViewport& viewport, bool minimized, bool maximized) io.IniFilename = GContext->IO.iniFile.c_str(); } + if(GContext->IO.kbdNavigation) + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + if (GContext->IO.docking) io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;