diff --git a/.github/workflows/BuildWheels.yml b/.github/workflows/BuildWheels.yml new file mode 100644 index 000000000..40b22d4f8 --- /dev/null +++ b/.github/workflows/BuildWheels.yml @@ -0,0 +1,156 @@ +name: Build Wheels + +on: + push: + branches: [master] + workflow_dispatch: + inputs: + version: + description: 'Version tag (e.g., v2.1.1-custom)' + required: false + default: 'v2.1.1-custom' + +jobs: + build-windows-x64: + runs-on: windows-2022 + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Build Wheel + shell: cmd + run: | + python -m pip install --upgrade pip wheel setuptools + python -m setup bdist_wheel --plat-name win_amd64 --dist-dir dist + + - uses: actions/upload-artifact@v4 + with: + name: windows-x64-py${{ matrix.python-version }} + path: dist/*.whl + + build-macos-arm64: + runs-on: macos-14 + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Build Wheel + run: | + python -m pip install --upgrade pip wheel setuptools + python -m setup bdist_wheel --plat-name macosx-14.0-arm64 --dist-dir dist + + - uses: actions/upload-artifact@v4 + with: + name: macos-arm64-py${{ matrix.python-version }} + path: dist/*.whl + + build-linux-x64: + runs-on: ubuntu-22.04 + env: + CXX: g++-10 + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libglu1-mesa-dev libgl1-mesa-dev xorg-dev libxrandr-dev + + - name: Build Wheel + run: | + python -m pip install --upgrade pip wheel setuptools + python -m setup bdist_wheel --plat-name manylinux2014_x86_64 --dist-dir dist + + - uses: actions/upload-artifact@v4 + with: + name: linux-x64-py${{ matrix.python-version }} + path: dist/*.whl + + build-linux-arm64: + runs-on: ubuntu-22.04-arm + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libglu1-mesa-dev libgl1-mesa-dev xorg-dev libxrandr-dev + + - name: Build Wheel + run: | + python -m pip install --upgrade pip wheel setuptools + python -m setup bdist_wheel --plat-name manylinux2014_aarch64 --dist-dir dist + + - uses: actions/upload-artifact@v4 + with: + name: linux-arm64-py${{ matrix.python-version }} + path: dist/*.whl + + create-release: + needs: [build-windows-x64, build-macos-arm64, build-linux-x64, build-linux-arm64] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + path: wheels + merge-multiple: true + + - name: List wheels + run: ls -la wheels/ + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ github.event.inputs.version || 'latest' }} + name: DearPyGui Custom Build + body: | + Custom DearPyGui build with: + - SliderFloatRange2/SliderIntRange2 widgets + - DragFloatRange2/DragIntRange2 widgets + - Step snapping support + + Install with: + ``` + pip install + ``` + files: wheels/*.whl + draft: false + prerelease: true diff --git a/.gitignore b/.gitignore index 45385971a..492fc4088 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ src/CMakeFiles/ thirdparty/CMakeFiles/ thirdparty/Makefile thirdparty/cmake_install.cmake +thirdparty/imgui_test_engine/ diff --git a/.gitmodules b/.gitmodules index 22c3527e4..221d7f4d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,11 +1,11 @@ [submodule "thirdparty/imgui"] path = thirdparty/imgui - url = https://github.com/ocornut/imgui + url = https://github.com/Entrpi/imgui branch = docking [submodule "thirdparty/implot"] path = thirdparty/implot - url = https://github.com/epezent/implot - branch = master + url = https://github.com/Entrpi/implot + branch = imgui-1.92-compat [submodule "thirdparty/cpython"] path = thirdparty/cpython url = https://github.com/python/cpython diff --git a/dearpygui/dearpygui.py b/dearpygui/dearpygui.py index aa4c2d846..e5154699b 100644 --- a/dearpygui/dearpygui.py +++ b/dearpygui/dearpygui.py @@ -4175,6 +4175,174 @@ def add_drag_intx(*, label: str =None, user_data: Any =None, use_internal_label: return internal_dpg.add_drag_intx(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, 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, size=size, format=format, speed=speed, min_value=min_value, max_value=max_value, no_input=no_input, clamped=clamped, **kwargs) +def add_drag_int_range(*, 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, source: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', callback: Callable =None, drag_callback: Callable =None, drop_callback: Callable =None, show: bool =True, enabled: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', tracked: bool =False, track_offset: float =0.5, default_value: Union[List[int], Tuple[int, ...]] =(0, 100), format: str ='%d', format_max: str ='', speed: float =1.0, min_value: int =0, max_value: int =100, no_input: bool =False, clamped: bool =False, **kwargs) -> Union[int, str]: + """ Adds a drag widget for a range of two int values (min and max). The min value cannot exceed max and vice versa. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + width (int, optional): Width of the item. + indent (int, optional): Offsets the widget to the right the specified number multiplied by the indent style. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + source (Union[int, str], optional): Overrides 'id' as value storage key. + payload_type (str, optional): Sender string type must be the same as the target for the target to run the payload_callback. + callback (Callable, optional): Registers a callback. + drag_callback (Callable, optional): Registers a drag callback for drag and drop. + drop_callback (Callable, optional): Registers a drop callback for drag and drop. + show (bool, optional): Attempt to render widget. + enabled (bool, optional): Turns off functionality of widget and applies the disabled theme. + pos (Union[List[int], Tuple[int, ...]], optional): Places the item relative to window coordinates, [0,0] is top left. + filter_key (str, optional): Used by filter widget. + tracked (bool, optional): Scroll tracking + track_offset (float, optional): 0.0f:top, 0.5f:center, 1.0f:bottom + default_value (Union[List[int], Tuple[int, ...]], optional): Initial (min, max) range values. + format (str, optional): Determines the format the values will be displayed as. + format_max (str, optional): Format for the max value (uses format if empty). + speed (float, optional): Sets the sensitivity while dragging. + min_value (int, optional): Minimum allowed value for the range. + max_value (int, optional): Maximum allowed value for the range. + no_input (bool, optional): Disable direct entry methods. + clamped (bool, optional): Apply min/max limits to direct entry. + id (Union[int, str], optional): (deprecated) + Returns: + Union[int, str] + """ + + if 'id' in kwargs.keys(): + warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) + tag=kwargs['id'] + + return internal_dpg.add_drag_int_range(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, 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, format=format, format_max=format_max, speed=speed, min_value=min_value, max_value=max_value, no_input=no_input, clamped=clamped, **kwargs) + +def add_drag_float_range(*, 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, source: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', callback: Callable =None, drag_callback: Callable =None, drop_callback: Callable =None, show: bool =True, enabled: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', tracked: bool =False, track_offset: float =0.5, default_value: Union[List[float], Tuple[float, ...]] =(0.0, 100.0), format: str ='%.3f', format_max: str ='', speed: float =1.0, min_value: float =0.0, max_value: float =100.0, no_input: bool =False, clamped: bool =False, **kwargs) -> Union[int, str]: + """ Adds a drag widget for a range of two float values (min and max). The min value cannot exceed max and vice versa. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + width (int, optional): Width of the item. + indent (int, optional): Offsets the widget to the right the specified number multiplied by the indent style. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + source (Union[int, str], optional): Overrides 'id' as value storage key. + payload_type (str, optional): Sender string type must be the same as the target for the target to run the payload_callback. + callback (Callable, optional): Registers a callback. + drag_callback (Callable, optional): Registers a drag callback for drag and drop. + drop_callback (Callable, optional): Registers a drop callback for drag and drop. + show (bool, optional): Attempt to render widget. + enabled (bool, optional): Turns off functionality of widget and applies the disabled theme. + pos (Union[List[int], Tuple[int, ...]], optional): Places the item relative to window coordinates, [0,0] is top left. + filter_key (str, optional): Used by filter widget. + tracked (bool, optional): Scroll tracking + track_offset (float, optional): 0.0f:top, 0.5f:center, 1.0f:bottom + default_value (Union[List[float], Tuple[float, ...]], optional): Initial (min, max) range values. + format (str, optional): Determines the format the values will be displayed as. + format_max (str, optional): Format for the max value (uses format if empty). + speed (float, optional): Sets the sensitivity while dragging. + min_value (float, optional): Minimum allowed value for the range. + max_value (float, optional): Maximum allowed value for the range. + no_input (bool, optional): Disable direct entry methods. + clamped (bool, optional): Apply min/max limits to direct entry. + id (Union[int, str], optional): (deprecated) + Returns: + Union[int, str] + """ + + if 'id' in kwargs.keys(): + warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) + tag=kwargs['id'] + + return internal_dpg.add_drag_float_range(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, 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, format=format, format_max=format_max, speed=speed, min_value=min_value, max_value=max_value, no_input=no_input, clamped=clamped, **kwargs) + +def add_slider_int_range(*, 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, source: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', callback: Callable =None, drag_callback: Callable =None, drop_callback: Callable =None, show: bool =True, enabled: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', tracked: bool =False, track_offset: float =0.5, default_value: Union[List[int], Tuple[int, ...]] =(0, 100), format: str ='%d', format_max: str ='', min_value: int =0, max_value: int =100, no_input: bool =False, clamped: bool =False, step: int =0, **kwargs) -> Union[int, str]: + """ Adds a visual slider for selecting a range of two int values. Features a colored bar showing the selected range with two draggable handles. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + width (int, optional): Width of the item. + indent (int, optional): Offsets the widget to the right the specified number multiplied by the indent style. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + source (Union[int, str], optional): Overrides 'id' as value storage key. + payload_type (str, optional): Sender string type must be the same as the target for the target to run the payload_callback. + callback (Callable, optional): Registers a callback. + drag_callback (Callable, optional): Registers a drag callback for drag and drop. + drop_callback (Callable, optional): Registers a drop callback for drag and drop. + show (bool, optional): Attempt to render widget. + enabled (bool, optional): Turns off functionality of widget and applies the disabled theme. + pos (Union[List[int], Tuple[int, ...]], optional): Places the item relative to window coordinates, [0,0] is top left. + filter_key (str, optional): Used by filter widget. + tracked (bool, optional): Scroll tracking + track_offset (float, optional): 0.0f:top, 0.5f:center, 1.0f:bottom + default_value (Union[List[int], Tuple[int, ...]], optional): Initial (min, max) range values. + format (str, optional): Determines the format the values will be displayed as. + format_max (str, optional): Format for the max value (uses format if empty). + min_value (int, optional): Minimum allowed value for the range. + max_value (int, optional): Maximum allowed value for the range. + no_input (bool, optional): Disable direct entry methods. + clamped (bool, optional): Apply min/max limits to direct entry. + step (int, optional): Snap values to multiples of step (0 disables snapping). + id (Union[int, str], optional): (deprecated) + Returns: + Union[int, str] + """ + + if 'id' in kwargs.keys(): + warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) + tag=kwargs['id'] + + return internal_dpg.add_slider_int_range(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, 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, format=format, format_max=format_max, min_value=min_value, max_value=max_value, no_input=no_input, clamped=clamped, step=step, **kwargs) + +def add_slider_float_range(*, 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, source: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', callback: Callable =None, drag_callback: Callable =None, drop_callback: Callable =None, show: bool =True, enabled: bool =True, pos: Union[List[int], Tuple[int, ...]] =[], filter_key: str ='', tracked: bool =False, track_offset: float =0.5, default_value: Union[List[float], Tuple[float, ...]] =(0.0, 100.0), format: str ='%.3f', format_max: str ='', min_value: float =0.0, max_value: float =100.0, no_input: bool =False, clamped: bool =False, step: float =0.0, **kwargs) -> Union[int, str]: + """ Adds a visual slider for selecting a range of two float values. Features a colored bar showing the selected range with two draggable handles. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + width (int, optional): Width of the item. + indent (int, optional): Offsets the widget to the right the specified number multiplied by the indent style. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + source (Union[int, str], optional): Overrides 'id' as value storage key. + payload_type (str, optional): Sender string type must be the same as the target for the target to run the payload_callback. + callback (Callable, optional): Registers a callback. + drag_callback (Callable, optional): Registers a drag callback for drag and drop. + drop_callback (Callable, optional): Registers a drop callback for drag and drop. + show (bool, optional): Attempt to render widget. + enabled (bool, optional): Turns off functionality of widget and applies the disabled theme. + pos (Union[List[int], Tuple[int, ...]], optional): Places the item relative to window coordinates, [0,0] is top left. + filter_key (str, optional): Used by filter widget. + tracked (bool, optional): Scroll tracking + track_offset (float, optional): 0.0f:top, 0.5f:center, 1.0f:bottom + default_value (Union[List[float], Tuple[float, ...]], optional): Initial (min, max) range values. + format (str, optional): Determines the format the values will be displayed as. + format_max (str, optional): Format for the max value (uses format if empty). + min_value (float, optional): Minimum allowed value for the range. + max_value (float, optional): Maximum allowed value for the range. + no_input (bool, optional): Disable direct entry methods. + clamped (bool, optional): Apply min/max limits to direct entry. + step (float, optional): Snap values to multiples of step (0 disables snapping). + id (Union[int, str], optional): (deprecated) + Returns: + Union[int, str] + """ + + if 'id' in kwargs.keys(): + warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) + tag=kwargs['id'] + + return internal_dpg.add_slider_float_range(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, 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, format=format, format_max=format_max, min_value=min_value, max_value=max_value, no_input=no_input, clamped=clamped, step=step, **kwargs) + def add_drag_line(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, before: Union[int, str] =0, source: Union[int, str] =0, callback: Callable =None, show: bool =True, default_value: float =0.0, color: Union[List[int], Tuple[int, ...]] =(0, 0, 0, -255), thickness: float =1.0, show_label: bool =True, vertical: bool =True, delayed: bool =False, no_cursor: bool =False, no_fit: bool =False, no_inputs: bool =False, **kwargs) -> Union[int, str]: """ Adds a drag line to a plot. diff --git a/issue_76_comment.md b/issue_76_comment.md new file mode 100644 index 000000000..3a98b9736 --- /dev/null +++ b/issue_76_comment.md @@ -0,0 +1,33 @@ +# Draft Comment for Issue #76 + +--- + +I've been working on a `SliderFloatRange2`/`SliderIntRange2` implementation and wanted to share it for feedback before formalizing a PR. + +**Implementation approach:** + +- **Template-based `SliderBehaviorRangeT<>`** following the existing `SliderBehaviorT` pattern +- **Reuses `ScaleRatioFromValueT`/`ScaleValueFromRatioT`** for value conversion (no duplication of that logic) +- **`GetStateStorage()->GetIntRef()`** for per-widget active handle tracking (no static variables) +- **Keyboard/gamepad navigation**: Up/Down switches between handles, Left/Right adjusts the active handle value +- **Ctrl+Click text input** via `TempInputScalar` (edits whichever handle is closer to click) +- **Single `SliderScalarRange2`** handles all data types; `SliderFloatRange2`/`SliderIntRange2` are thin wrappers + +**Interaction model:** Uses click-to-position like standard sliders (not relative dragging like `DragFloatRange2`). On click, the nearest handle is selected and follows the mouse. Handles cannot cross each other. + +**Code size:** ~250 lines for the behavior template + widget function, with float/int sharing the same code path. + +**What it doesn't do:** +- Middle-bar dragging to move both handles together (could be added) +- Vertical slider variant (straightforward to add with axis flag) + +Reference implementation: https://github.com/Entrpi/imgui/blob/docking/imgui_widgets_range.cpp + +Would this direction be acceptable, or would you prefer a different interaction model (e.g., relative dragging)? Happy to adjust based on feedback. + +--- + +**Notes for review:** +- The reference file shows the implementation without any extensions +- Tested with DearPyGui bindings - keyboard nav and Ctrl+Click work correctly +- Logarithmic scale support included diff --git a/patches/0001-fix-get_plot_mouse_pos-on-demand-calculation.patch b/patches/0001-fix-get_plot_mouse_pos-on-demand-calculation.patch new file mode 100644 index 000000000..70a541736 --- /dev/null +++ b/patches/0001-fix-get_plot_mouse_pos-on-demand-calculation.patch @@ -0,0 +1,125 @@ +diff --git a/src/dearpygui_commands.h b/src/dearpygui_commands.h +index e25355b..e3cdf53 100644 +--- a/src/dearpygui_commands.h ++++ b/src/dearpygui_commands.h +@@ -2775,12 +2775,56 @@ get_mouse_pos(PyObject* self, PyObject* args, PyObject* kwargs) + static PyObject* + get_plot_mouse_pos(PyObject* self, PyObject* args, PyObject* kwargs) + { ++ PyObject* plotraw = nullptr; + +- if (!Parse((GetParsers())["get_plot_mouse_pos"], args, kwargs, __FUNCTION__)) ++ if (!Parse((GetParsers())["get_plot_mouse_pos"], args, kwargs, __FUNCTION__, &plotraw)) + return GetPyNone(); + +- mvVec2 pos = { (f32)GContext->input.mousePlotPos.x, (f32)GContext->input.mousePlotPos.y }; ++ // If plot specified, calculate position on-demand using cached geometry ++ mvUUID plotId = plotraw ? GetIDFromPyObject(plotraw) : 0; ++ if (plotId != 0) ++ { ++ mvPySafeLockGuard lk(GContext->mutex); ++ auto aplot = GetItem(*GContext->itemRegistry, plotId); ++ if (aplot == nullptr) ++ { ++ mvThrowPythonError(mvErrorCode::mvItemNotFound, "get_plot_mouse_pos", ++ "Item not found: " + std::to_string(plotId), nullptr); ++ return GetPyNone(); ++ } ++ ++ if (aplot->type != mvAppItemType::mvPlot) ++ { ++ mvThrowPythonError(mvErrorCode::mvIncompatibleType, "get_plot_mouse_pos", ++ "Incompatible type. Expected types include: mvPlot", aplot); ++ return GetPyNone(); ++ } + ++ mvPlot* plot = static_cast(aplot); ++ const auto& cfg = plot->configData; ++ ++ // Get current mouse position ++ ImVec2 mousePos = ImGui::GetMousePos(); ++ ++ // Calculate normalized position within plot data area ++ float plotWidth = cfg._plotRectMax.x - cfg._plotRectMin.x; ++ float plotHeight = cfg._plotRectMax.y - cfg._plotRectMin.y; ++ ++ if (plotWidth <= 0 || plotHeight <= 0) ++ return ToPyPair(0.0, 0.0); ++ ++ float normX = (mousePos.x - cfg._plotRectMin.x) / plotWidth; ++ float normY = (mousePos.y - cfg._plotRectMin.y) / plotHeight; ++ ++ // Convert to plot coordinates (note: Y is inverted in screen coords) ++ double plotX = cfg._xAxisMin + normX * (cfg._xAxisMax - cfg._xAxisMin); ++ double plotY = cfg._yAxisMax - normY * (cfg._yAxisMax - cfg._yAxisMin); ++ ++ return ToPyPair(plotX, plotY); ++ } ++ ++ // No plot specified - return legacy cached value ++ mvVec2 pos = { (f32)GContext->input.mousePlotPos.x, (f32)GContext->input.mousePlotPos.y }; + return ToPyPair(pos.x, pos.y); + } + +diff --git a/src/dearpygui_parsers.h b/src/dearpygui_parsers.h +index 8446225..84ea228 100644 +--- a/src/dearpygui_parsers.h ++++ b/src/dearpygui_parsers.h +@@ -806,11 +806,12 @@ InsertParser_Block1(std::map& parsers) + + { + std::vector args; ++ args.push_back({ mvPyDataType::UUID, "plot", mvArgType::KEYWORD_ARG, "0", "Plot to query. When specified, calculates position on-demand using current mouse position and plot geometry. When not specified, returns cached position from last hovered plot (legacy behavior)." }); + + mvPythonParserSetup setup; +- setup.about = "Returns mouse position in plot."; ++ setup.about = "Returns mouse position in plot coordinates. For accurate real-time tracking, pass the plot tag."; + setup.category = { "Input Polling" }; +- setup.returnType = mvPyDataType::IntList; ++ setup.returnType = mvPyDataType::FloatList; + + mvPythonParser parser = FinalizeParser(setup, args); + parsers.insert({ "get_plot_mouse_pos", parser }); +diff --git a/src/mvPlotting.cpp b/src/mvPlotting.cpp +index 6eb507e..5406ed8 100644 +--- a/src/mvPlotting.cpp ++++ b/src/mvPlotting.cpp +@@ -592,6 +592,19 @@ DearPyGui::draw_plot(ImDrawList* drawlist, mvAppItem& item, mvPlotConfig& config + }); + } + ++ // Cache plot geometry for on-demand mouse position calculation ++ { ++ ImVec2 plotPos = ImPlot::GetPlotPos(); ++ ImVec2 plotSize = ImPlot::GetPlotSize(); ++ config._plotRectMin = plotPos; ++ config._plotRectMax = ImVec2(plotPos.x + plotSize.x, plotPos.y + plotSize.y); ++ ImPlotRect limits = ImPlot::GetPlotLimits(); ++ config._xAxisMin = limits.X.Min; ++ config._xAxisMax = limits.X.Max; ++ config._yAxisMin = limits.Y.Min; ++ config._yAxisMax = limits.Y.Max; ++ } ++ + if (ImPlot::IsPlotHovered()) + { + GContext->input.mousePlotPos.x = ImPlot::GetPlotMousePos().x; +diff --git a/src/mvPlotting.h b/src/mvPlotting.h +index 9d35d90..0b15cfd 100644 +--- a/src/mvPlotting.h ++++ b/src/mvPlotting.h +@@ -437,6 +437,14 @@ struct mvPlotConfig + bool localTime = false; + bool iSO8601 = false; + bool clock24Hour = false; ++ ++ // Cached plot geometry for on-demand mouse position calculation ++ ImVec2 _plotRectMin = {0, 0}; // data area top-left (screen coords) ++ ImVec2 _plotRectMax = {0, 0}; // data area bottom-right (screen coords) ++ double _xAxisMin = 0; ++ double _xAxisMax = 1; ++ double _yAxisMin = 0; ++ double _yAxisMax = 1; + }; + + //----------------------------------------------------------------------------- diff --git a/src/dearpygui_commands.h b/src/dearpygui_commands.h index e25355bc2..e3cdf535e 100644 --- a/src/dearpygui_commands.h +++ b/src/dearpygui_commands.h @@ -2775,12 +2775,56 @@ get_mouse_pos(PyObject* self, PyObject* args, PyObject* kwargs) static PyObject* get_plot_mouse_pos(PyObject* self, PyObject* args, PyObject* kwargs) { + PyObject* plotraw = nullptr; - if (!Parse((GetParsers())["get_plot_mouse_pos"], args, kwargs, __FUNCTION__)) + if (!Parse((GetParsers())["get_plot_mouse_pos"], args, kwargs, __FUNCTION__, &plotraw)) return GetPyNone(); - mvVec2 pos = { (f32)GContext->input.mousePlotPos.x, (f32)GContext->input.mousePlotPos.y }; + // If plot specified, calculate position on-demand using cached geometry + mvUUID plotId = plotraw ? GetIDFromPyObject(plotraw) : 0; + if (plotId != 0) + { + mvPySafeLockGuard lk(GContext->mutex); + auto aplot = GetItem(*GContext->itemRegistry, plotId); + if (aplot == nullptr) + { + mvThrowPythonError(mvErrorCode::mvItemNotFound, "get_plot_mouse_pos", + "Item not found: " + std::to_string(plotId), nullptr); + return GetPyNone(); + } + + if (aplot->type != mvAppItemType::mvPlot) + { + mvThrowPythonError(mvErrorCode::mvIncompatibleType, "get_plot_mouse_pos", + "Incompatible type. Expected types include: mvPlot", aplot); + return GetPyNone(); + } + mvPlot* plot = static_cast(aplot); + const auto& cfg = plot->configData; + + // Get current mouse position + ImVec2 mousePos = ImGui::GetMousePos(); + + // Calculate normalized position within plot data area + float plotWidth = cfg._plotRectMax.x - cfg._plotRectMin.x; + float plotHeight = cfg._plotRectMax.y - cfg._plotRectMin.y; + + if (plotWidth <= 0 || plotHeight <= 0) + return ToPyPair(0.0, 0.0); + + float normX = (mousePos.x - cfg._plotRectMin.x) / plotWidth; + float normY = (mousePos.y - cfg._plotRectMin.y) / plotHeight; + + // Convert to plot coordinates (note: Y is inverted in screen coords) + double plotX = cfg._xAxisMin + normX * (cfg._xAxisMax - cfg._xAxisMin); + double plotY = cfg._yAxisMax - normY * (cfg._yAxisMax - cfg._yAxisMin); + + return ToPyPair(plotX, plotY); + } + + // No plot specified - return legacy cached value + mvVec2 pos = { (f32)GContext->input.mousePlotPos.x, (f32)GContext->input.mousePlotPos.y }; return ToPyPair(pos.x, pos.y); } diff --git a/src/dearpygui_parsers.h b/src/dearpygui_parsers.h index 8446225ed..84ea2285a 100644 --- a/src/dearpygui_parsers.h +++ b/src/dearpygui_parsers.h @@ -806,11 +806,12 @@ InsertParser_Block1(std::map& parsers) { std::vector args; + args.push_back({ mvPyDataType::UUID, "plot", mvArgType::KEYWORD_ARG, "0", "Plot to query. When specified, calculates position on-demand using current mouse position and plot geometry. When not specified, returns cached position from last hovered plot (legacy behavior)." }); mvPythonParserSetup setup; - setup.about = "Returns mouse position in plot."; + setup.about = "Returns mouse position in plot coordinates. For accurate real-time tracking, pass the plot tag."; setup.category = { "Input Polling" }; - setup.returnType = mvPyDataType::IntList; + setup.returnType = mvPyDataType::FloatList; mvPythonParser parser = FinalizeParser(setup, args); parsers.insert({ "get_plot_mouse_pos", parser }); diff --git a/src/mvAboutWindow.cpp b/src/mvAboutWindow.cpp index 897458cbf..187d7e117 100644 --- a/src/mvAboutWindow.cpp +++ b/src/mvAboutWindow.cpp @@ -136,7 +136,7 @@ void mvAboutWindow::drawWidgets() if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset"); ImGui::Separator(); - ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); + ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height); ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); ImGui::Separator(); diff --git a/src/mvAppItem.cpp b/src/mvAppItem.cpp index 4815f8797..004cfc0c2 100644 --- a/src/mvAppItem.cpp +++ b/src/mvAppItem.cpp @@ -89,12 +89,24 @@ void mvAppItem::submitCallback(std::array app_data) submitCallbackEx([app_data=std::move(app_data)]() { return ToPyIntList(app_data.data(), (int) app_data.size()); }); } +template<> +void mvAppItem::submitCallback(std::array app_data) +{ + submitCallbackEx([app_data=std::move(app_data)]() { return ToPyIntList(app_data.data(), (int) app_data.size()); }); +} + template<> void mvAppItem::submitCallback(std::array app_data) { submitCallbackEx([app_data=std::move(app_data)]() { return ToPyFloatList(app_data.data(), (int) app_data.size()); }); } +template<> +void mvAppItem::submitCallback(std::array app_data) +{ + submitCallbackEx([app_data=std::move(app_data)]() { return ToPyFloatList(app_data.data(), (int) app_data.size()); }); +} + template<> void mvAppItem::submitCallback(std::array app_data) { @@ -3731,6 +3743,134 @@ DearPyGui::GetEntityParser(mvAppItemType type) setup.about = "Adds drag input for a set of int values up to 4. Directly entry can be done with double click or CTRL+Click. Min and Max alone are a soft limit for the drag. Use clamped keyword to also apply limits to the direct entry modes."; break; } + case mvAppItemType::mvDragIntRange: + { + AddCommonArgs(args, (CommonParserArgs)( + MV_PARSER_ARG_ID | + MV_PARSER_ARG_WIDTH | + MV_PARSER_ARG_INDENT | + MV_PARSER_ARG_PARENT | + MV_PARSER_ARG_BEFORE | + MV_PARSER_ARG_SOURCE | + MV_PARSER_ARG_CALLBACK | + MV_PARSER_ARG_SHOW | + MV_PARSER_ARG_ENABLED | + MV_PARSER_ARG_FILTER | + MV_PARSER_ARG_DROP_CALLBACK | + MV_PARSER_ARG_DRAG_CALLBACK | + MV_PARSER_ARG_PAYLOAD_TYPE | + MV_PARSER_ARG_TRACKED | + MV_PARSER_ARG_POS) + ); + + args.push_back({ mvPyDataType::IntList, "default_value", mvArgType::KEYWORD_ARG, "(0, 100)", "Initial (min, max) range values." }); + args.push_back({ mvPyDataType::String, "format", mvArgType::KEYWORD_ARG, "'%d'", "Determines the format the values will be displayed as." }); + args.push_back({ mvPyDataType::String, "format_max", mvArgType::KEYWORD_ARG, "''", "Format for the max value (uses format if empty)." }); + args.push_back({ mvPyDataType::Float, "speed", mvArgType::KEYWORD_ARG, "1.0", "Sets the sensitivity while dragging." }); + args.push_back({ mvPyDataType::Integer, "min_value", mvArgType::KEYWORD_ARG, "0", "Minimum allowed value for the range." }); + args.push_back({ mvPyDataType::Integer, "max_value", mvArgType::KEYWORD_ARG, "100", "Maximum allowed value for the range." }); + args.push_back({ mvPyDataType::Bool, "no_input", mvArgType::KEYWORD_ARG, "False", "Disable direct entry methods." }); + args.push_back({ mvPyDataType::Bool, "clamped", mvArgType::KEYWORD_ARG, "False", "Apply min/max limits to direct entry." }); + + setup.about = "Adds a drag widget for a range of two int values (min and max). The min value cannot exceed max and vice versa."; + break; + } + case mvAppItemType::mvDragFloatRange: + { + AddCommonArgs(args, (CommonParserArgs)( + MV_PARSER_ARG_ID | + MV_PARSER_ARG_WIDTH | + MV_PARSER_ARG_INDENT | + MV_PARSER_ARG_PARENT | + MV_PARSER_ARG_BEFORE | + MV_PARSER_ARG_SOURCE | + MV_PARSER_ARG_CALLBACK | + MV_PARSER_ARG_SHOW | + MV_PARSER_ARG_ENABLED | + MV_PARSER_ARG_FILTER | + MV_PARSER_ARG_DROP_CALLBACK | + MV_PARSER_ARG_DRAG_CALLBACK | + MV_PARSER_ARG_PAYLOAD_TYPE | + MV_PARSER_ARG_TRACKED | + MV_PARSER_ARG_POS) + ); + + args.push_back({ mvPyDataType::FloatList, "default_value", mvArgType::KEYWORD_ARG, "(0.0, 100.0)", "Initial (min, max) range values." }); + args.push_back({ mvPyDataType::String, "format", mvArgType::KEYWORD_ARG, "'%.3f'", "Determines the format the values will be displayed as." }); + args.push_back({ mvPyDataType::String, "format_max", mvArgType::KEYWORD_ARG, "''", "Format for the max value (uses format if empty)." }); + args.push_back({ mvPyDataType::Float, "speed", mvArgType::KEYWORD_ARG, "1.0", "Sets the sensitivity while dragging." }); + args.push_back({ mvPyDataType::Float, "min_value", mvArgType::KEYWORD_ARG, "0.0", "Minimum allowed value for the range." }); + args.push_back({ mvPyDataType::Float, "max_value", mvArgType::KEYWORD_ARG, "100.0", "Maximum allowed value for the range." }); + args.push_back({ mvPyDataType::Bool, "no_input", mvArgType::KEYWORD_ARG, "False", "Disable direct entry methods." }); + args.push_back({ mvPyDataType::Bool, "clamped", mvArgType::KEYWORD_ARG, "False", "Apply min/max limits to direct entry." }); + + setup.about = "Adds a drag widget for a range of two float values (min and max). The min value cannot exceed max and vice versa."; + break; + } + case mvAppItemType::mvSliderIntRange: + { + AddCommonArgs(args, (CommonParserArgs)( + MV_PARSER_ARG_ID | + MV_PARSER_ARG_WIDTH | + MV_PARSER_ARG_INDENT | + MV_PARSER_ARG_PARENT | + MV_PARSER_ARG_BEFORE | + MV_PARSER_ARG_SOURCE | + MV_PARSER_ARG_CALLBACK | + MV_PARSER_ARG_SHOW | + MV_PARSER_ARG_ENABLED | + MV_PARSER_ARG_FILTER | + MV_PARSER_ARG_DROP_CALLBACK | + MV_PARSER_ARG_DRAG_CALLBACK | + MV_PARSER_ARG_PAYLOAD_TYPE | + MV_PARSER_ARG_TRACKED | + MV_PARSER_ARG_POS) + ); + + args.push_back({ mvPyDataType::IntList, "default_value", mvArgType::KEYWORD_ARG, "(0, 100)", "Initial (min, max) range values." }); + args.push_back({ mvPyDataType::String, "format", mvArgType::KEYWORD_ARG, "'%d'", "Determines the format the values will be displayed as." }); + args.push_back({ mvPyDataType::String, "format_max", mvArgType::KEYWORD_ARG, "''", "Format for the max value (uses format if empty)." }); + args.push_back({ mvPyDataType::Integer, "min_value", mvArgType::KEYWORD_ARG, "0", "Minimum allowed value for the range." }); + args.push_back({ mvPyDataType::Integer, "max_value", mvArgType::KEYWORD_ARG, "100", "Maximum allowed value for the range." }); + args.push_back({ mvPyDataType::Integer, "step", mvArgType::KEYWORD_ARG, "0", "Snap values to multiples of step (0 disables snapping)." }); + args.push_back({ mvPyDataType::Bool, "no_input", mvArgType::KEYWORD_ARG, "False", "Disable direct entry methods." }); + args.push_back({ mvPyDataType::Bool, "clamped", mvArgType::KEYWORD_ARG, "False", "Apply min/max limits to direct entry." }); + + setup.about = "Adds a visual slider for selecting a range of two int values. Features a colored bar showing the selected range with two draggable handles."; + break; + } + case mvAppItemType::mvSliderFloatRange: + { + AddCommonArgs(args, (CommonParserArgs)( + MV_PARSER_ARG_ID | + MV_PARSER_ARG_WIDTH | + MV_PARSER_ARG_INDENT | + MV_PARSER_ARG_PARENT | + MV_PARSER_ARG_BEFORE | + MV_PARSER_ARG_SOURCE | + MV_PARSER_ARG_CALLBACK | + MV_PARSER_ARG_SHOW | + MV_PARSER_ARG_ENABLED | + MV_PARSER_ARG_FILTER | + MV_PARSER_ARG_DROP_CALLBACK | + MV_PARSER_ARG_DRAG_CALLBACK | + MV_PARSER_ARG_PAYLOAD_TYPE | + MV_PARSER_ARG_TRACKED | + MV_PARSER_ARG_POS) + ); + + args.push_back({ mvPyDataType::FloatList, "default_value", mvArgType::KEYWORD_ARG, "(0.0, 100.0)", "Initial (min, max) range values." }); + args.push_back({ mvPyDataType::String, "format", mvArgType::KEYWORD_ARG, "'%.3f'", "Determines the format the values will be displayed as." }); + args.push_back({ mvPyDataType::String, "format_max", mvArgType::KEYWORD_ARG, "''", "Format for the max value (uses format if empty)." }); + args.push_back({ mvPyDataType::Float, "min_value", mvArgType::KEYWORD_ARG, "0.0", "Minimum allowed value for the range." }); + args.push_back({ mvPyDataType::Float, "max_value", mvArgType::KEYWORD_ARG, "100.0", "Maximum allowed value for the range." }); + args.push_back({ mvPyDataType::Float, "step", mvArgType::KEYWORD_ARG, "0.0", "Snap values to multiples of step (0 disables snapping)." }); + args.push_back({ mvPyDataType::Bool, "no_input", mvArgType::KEYWORD_ARG, "False", "Disable direct entry methods." }); + args.push_back({ mvPyDataType::Bool, "clamped", mvArgType::KEYWORD_ARG, "False", "Apply min/max limits to direct entry." }); + + setup.about = "Adds a visual slider for selecting a range of two float values. Features a colored bar showing the selected range with two draggable handles."; + break; + } case mvAppItemType::mvSliderFloatMulti: { AddCommonArgs(args, (CommonParserArgs)( diff --git a/src/mvAppItem.h b/src/mvAppItem.h index aa125b789..c92abaf5c 100644 --- a/src/mvAppItem.h +++ b/src/mvAppItem.h @@ -355,6 +355,10 @@ GetEntityCommand(mvAppItemType type) case mvAppItemType::mvDragFloatMulti: return "add_drag_floatx"; case mvAppItemType::mvDragDoubleMulti: return "add_drag_doublex"; case mvAppItemType::mvDragIntMulti: return "add_drag_intx"; + case mvAppItemType::mvDragIntRange: return "add_drag_int_range"; + case mvAppItemType::mvDragFloatRange: return "add_drag_float_range"; + case mvAppItemType::mvSliderIntRange: return "add_slider_int_range"; + case mvAppItemType::mvSliderFloatRange: return "add_slider_float_range"; case mvAppItemType::mvSliderFloatMulti: return "add_slider_floatx"; case mvAppItemType::mvSliderDoubleMulti: return "add_slider_doublex"; case mvAppItemType::mvSliderIntMulti: return "add_slider_intx"; diff --git a/src/mvAppItemTypes.inc b/src/mvAppItemTypes.inc index 5ecffb450..58a2e1a92 100644 --- a/src/mvAppItemTypes.inc +++ b/src/mvAppItemTypes.inc @@ -168,6 +168,10 @@ X( mvInputDoubleMulti ) \ X( mvDragDouble ) \ X( mvDragDoubleMulti ) \ + X( mvDragIntRange ) \ + X( mvDragFloatRange ) \ + X( mvSliderIntRange ) \ + X( mvSliderFloatRange ) \ X( mvSliderDouble ) \ X( mvSliderDoubleMulti ) \ X( mvCustomSeries ) diff --git a/src/mvBasicWidgets.cpp b/src/mvBasicWidgets.cpp index 6cf58f269..043ef3c77 100644 --- a/src/mvBasicWidgets.cpp +++ b/src/mvBasicWidgets.cpp @@ -186,6 +186,118 @@ DearPyGui::fill_configuration_dict(const mvDragIntMultiConfig& inConfig, PyObjec checkbitset("no_input", ImGuiSliderFlags_NoInput, inConfig.flags); } +void +DearPyGui::fill_configuration_dict(const mvDragIntRangeConfig& inConfig, PyObject* outDict) +{ + if (outDict == nullptr) + return; + + mvPyObject py_format = ToPyString(inConfig.format); + mvPyObject py_format_max = ToPyString(inConfig.format_max); + mvPyObject py_speed = ToPyFloat(inConfig.speed); + mvPyObject py_min_value = ToPyInt(inConfig.minv); + mvPyObject py_max_value = ToPyInt(inConfig.maxv); + + PyDict_SetItemString(outDict, "format", py_format); + PyDict_SetItemString(outDict, "format_max", py_format_max); + PyDict_SetItemString(outDict, "speed", py_speed); + PyDict_SetItemString(outDict, "min_value", py_min_value); + PyDict_SetItemString(outDict, "max_value", py_max_value); + + auto checkbitset = [outDict](const char* keyword, int flag, const int& flags) + { + mvPyObject py_result = ToPyBool(flags & flag); + PyDict_SetItemString(outDict, keyword, py_result); + }; + + checkbitset("clamped", ImGuiSliderFlags_AlwaysClamp, inConfig.flags); + checkbitset("no_input", ImGuiSliderFlags_NoInput, inConfig.flags); +} + +void +DearPyGui::fill_configuration_dict(const mvDragFloatRangeConfig& inConfig, PyObject* outDict) +{ + if (outDict == nullptr) + return; + + mvPyObject py_format = ToPyString(inConfig.format); + mvPyObject py_format_max = ToPyString(inConfig.format_max); + mvPyObject py_speed = ToPyFloat(inConfig.speed); + mvPyObject py_min_value = ToPyFloat(inConfig.minv); + mvPyObject py_max_value = ToPyFloat(inConfig.maxv); + + PyDict_SetItemString(outDict, "format", py_format); + PyDict_SetItemString(outDict, "format_max", py_format_max); + PyDict_SetItemString(outDict, "speed", py_speed); + PyDict_SetItemString(outDict, "min_value", py_min_value); + PyDict_SetItemString(outDict, "max_value", py_max_value); + + auto checkbitset = [outDict](const char* keyword, int flag, const int& flags) + { + mvPyObject py_result = ToPyBool(flags & flag); + PyDict_SetItemString(outDict, keyword, py_result); + }; + + checkbitset("clamped", ImGuiSliderFlags_AlwaysClamp, inConfig.flags); + checkbitset("no_input", ImGuiSliderFlags_NoInput, inConfig.flags); +} + +void +DearPyGui::fill_configuration_dict(const mvSliderIntRangeConfig& inConfig, PyObject* outDict) +{ + if (outDict == nullptr) + return; + + mvPyObject py_format = ToPyString(inConfig.format); + mvPyObject py_format_max = ToPyString(inConfig.format_max); + mvPyObject py_min_value = ToPyInt(inConfig.minv); + mvPyObject py_max_value = ToPyInt(inConfig.maxv); + mvPyObject py_step = ToPyInt(inConfig.step); + + PyDict_SetItemString(outDict, "format", py_format); + PyDict_SetItemString(outDict, "format_max", py_format_max); + PyDict_SetItemString(outDict, "min_value", py_min_value); + PyDict_SetItemString(outDict, "max_value", py_max_value); + PyDict_SetItemString(outDict, "step", py_step); + + auto checkbitset = [outDict](const char* keyword, int flag, const int& flags) + { + mvPyObject py_result = ToPyBool(flags & flag); + PyDict_SetItemString(outDict, keyword, py_result); + }; + + checkbitset("clamped", ImGuiSliderFlags_AlwaysClamp, inConfig.flags); + checkbitset("no_input", ImGuiSliderFlags_NoInput, inConfig.flags); +} + +void +DearPyGui::fill_configuration_dict(const mvSliderFloatRangeConfig& inConfig, PyObject* outDict) +{ + if (outDict == nullptr) + return; + + mvPyObject py_format = ToPyString(inConfig.format); + mvPyObject py_format_max = ToPyString(inConfig.format_max); + mvPyObject py_min_value = ToPyFloat(inConfig.minv); + mvPyObject py_max_value = ToPyFloat(inConfig.maxv); + mvPyObject py_step = ToPyFloat(inConfig.step); + + PyDict_SetItemString(outDict, "format", py_format); + PyDict_SetItemString(outDict, "format_max", py_format_max); + PyDict_SetItemString(outDict, "min_value", py_min_value); + PyDict_SetItemString(outDict, "max_value", py_max_value); + PyDict_SetItemString(outDict, "step", py_step); + + auto checkbitset = [outDict](const char* keyword, int flag, const int& flags) + { + mvPyObject py_result = ToPyBool(flags & flag); + PyDict_SetItemString(outDict, keyword, py_result); + }; + + checkbitset("clamped", ImGuiSliderFlags_AlwaysClamp, inConfig.flags); + checkbitset("no_input", ImGuiSliderFlags_NoInput, inConfig.flags); +} + void DearPyGui::fill_configuration_dict(const mvDragFloatMultiConfig& inConfig, PyObject* outDict) { @@ -892,6 +1004,150 @@ DearPyGui::set_configuration(PyObject* inDict, mvDragIntMultiConfig& outConfig, } } +void +DearPyGui::set_configuration(PyObject* inDict, mvDragIntRangeConfig& outConfig, mvAppItemInfo& info) +{ + if (inDict == nullptr) + return; + + if (PyObject* item = PyDict_GetItemString(inDict, "format")) outConfig.format = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "format_max")) outConfig.format_max = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "speed")) outConfig.speed = ToFloat(item); + if (PyObject* item = PyDict_GetItemString(inDict, "min_value")) outConfig.minv = ToInt(item); + if (PyObject* item = PyDict_GetItemString(inDict, "max_value")) outConfig.maxv = ToInt(item); + + auto flagop = [inDict](const char* keyword, int flag, int& flags) + { + if (PyObject* item = PyDict_GetItemString(inDict, keyword)) ToBool(item) ? flags |= flag : flags &= ~flag; + }; + + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.flags); + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.stor_flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.stor_flags); + + if (info.enabledLastFrame) + { + info.enabledLastFrame = false; + outConfig.flags = outConfig.stor_flags; + } + + if (info.disabledLastFrame) + { + info.disabledLastFrame = false; + outConfig.stor_flags = outConfig.flags; + outConfig.flags |= ImGuiSliderFlags_NoInput; + } +} + +void +DearPyGui::set_configuration(PyObject* inDict, mvDragFloatRangeConfig& outConfig, mvAppItemInfo& info) +{ + if (inDict == nullptr) + return; + + if (PyObject* item = PyDict_GetItemString(inDict, "format")) outConfig.format = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "format_max")) outConfig.format_max = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "speed")) outConfig.speed = ToFloat(item); + if (PyObject* item = PyDict_GetItemString(inDict, "min_value")) outConfig.minv = ToFloat(item); + if (PyObject* item = PyDict_GetItemString(inDict, "max_value")) outConfig.maxv = ToFloat(item); + + auto flagop = [inDict](const char* keyword, int flag, int& flags) + { + if (PyObject* item = PyDict_GetItemString(inDict, keyword)) ToBool(item) ? flags |= flag : flags &= ~flag; + }; + + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.flags); + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.stor_flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.stor_flags); + + if (info.enabledLastFrame) + { + info.enabledLastFrame = false; + outConfig.flags = outConfig.stor_flags; + } + + if (info.disabledLastFrame) + { + info.disabledLastFrame = false; + outConfig.stor_flags = outConfig.flags; + outConfig.flags |= ImGuiSliderFlags_NoInput; + } +} + +void +DearPyGui::set_configuration(PyObject* inDict, mvSliderIntRangeConfig& outConfig, mvAppItemInfo& info) +{ + if (inDict == nullptr) + return; + + if (PyObject* item = PyDict_GetItemString(inDict, "format")) outConfig.format = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "format_max")) outConfig.format_max = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "min_value")) outConfig.minv = ToInt(item); + if (PyObject* item = PyDict_GetItemString(inDict, "max_value")) outConfig.maxv = ToInt(item); + if (PyObject* item = PyDict_GetItemString(inDict, "step")) outConfig.step = ToInt(item); + + auto flagop = [inDict](const char* keyword, int flag, int& flags) + { + if (PyObject* item = PyDict_GetItemString(inDict, keyword)) ToBool(item) ? flags |= flag : flags &= ~flag; + }; + + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.flags); + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.stor_flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.stor_flags); + + if (info.enabledLastFrame) + { + info.enabledLastFrame = false; + outConfig.flags = outConfig.stor_flags; + } + + if (info.disabledLastFrame) + { + info.disabledLastFrame = false; + outConfig.stor_flags = outConfig.flags; + outConfig.flags |= ImGuiSliderFlags_NoInput; + } +} + +void +DearPyGui::set_configuration(PyObject* inDict, mvSliderFloatRangeConfig& outConfig, mvAppItemInfo& info) +{ + if (inDict == nullptr) + return; + + if (PyObject* item = PyDict_GetItemString(inDict, "format")) outConfig.format = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "format_max")) outConfig.format_max = ToString(item); + if (PyObject* item = PyDict_GetItemString(inDict, "min_value")) outConfig.minv = ToFloat(item); + if (PyObject* item = PyDict_GetItemString(inDict, "max_value")) outConfig.maxv = ToFloat(item); + if (PyObject* item = PyDict_GetItemString(inDict, "step")) outConfig.step = ToFloat(item); + + auto flagop = [inDict](const char* keyword, int flag, int& flags) + { + if (PyObject* item = PyDict_GetItemString(inDict, keyword)) ToBool(item) ? flags |= flag : flags &= ~flag; + }; + + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.flags); + flagop("clamped", ImGuiSliderFlags_AlwaysClamp, outConfig.stor_flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.flags); + flagop("no_input", ImGuiSliderFlags_NoInput, outConfig.stor_flags); + + if (info.enabledLastFrame) + { + info.enabledLastFrame = false; + outConfig.flags = outConfig.stor_flags; + } + + if (info.disabledLastFrame) + { + info.disabledLastFrame = false; + outConfig.stor_flags = outConfig.flags; + outConfig.flags |= ImGuiSliderFlags_NoInput; + } +} + void DearPyGui::set_configuration(PyObject* inDict, mvDragFloatMultiConfig& outConfig, mvAppItemInfo& info) { @@ -2109,7 +2365,7 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvDragFloatMultiC } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvDragDoubleMultiConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvDragIntRangeConfig& outConfig) { if (dataSource == item.config.source) return; item.config.source = dataSource; @@ -2127,11 +2383,11 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvDragDoubleMulti "Values types do not match: " + std::to_string(dataSource), &item); return; } - outConfig.value = *static_cast>*>(srcItem->getValue()); + outConfig.value = *static_cast>*>(srcItem->getValue()); } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvDragFloatRangeConfig& outConfig) { if (dataSource == item.config.source) return; item.config.source = dataSource; @@ -2149,11 +2405,11 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatConf "Values types do not match: " + std::to_string(dataSource), &item); return; } - outConfig.value = *static_cast*>(srcItem->getValue()); + outConfig.value = *static_cast>*>(srcItem->getValue()); } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatMultiConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntRangeConfig& outConfig) { if (dataSource == item.config.source) return; item.config.source = dataSource; @@ -2171,11 +2427,11 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatMult "Values types do not match: " + std::to_string(dataSource), &item); return; } - outConfig.value = *static_cast>*>(srcItem->getValue()); + outConfig.value = *static_cast>*>(srcItem->getValue()); } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderDoubleConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatRangeConfig& outConfig) { if (dataSource == item.config.source) return; item.config.source = dataSource; @@ -2193,11 +2449,11 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderDoubleCon "Values types do not match: " + std::to_string(dataSource), &item); return; } - outConfig.value = *static_cast*>(srcItem->getValue()); + outConfig.value = *static_cast>*>(srcItem->getValue()); } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderDoubleMultiConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvDragDoubleMultiConfig& outConfig) { if (dataSource == item.config.source) return; item.config.source = dataSource; @@ -2219,7 +2475,7 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderDoubleMul } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatConfig& outConfig) { if (dataSource == item.config.source) return; item.config.source = dataSource; @@ -2237,11 +2493,11 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntConfig "Values types do not match: " + std::to_string(dataSource), &item); return; } - outConfig.value = *static_cast*>(srcItem->getValue()); + outConfig.value = *static_cast*>(srcItem->getValue()); } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntMultiConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatMultiConfig& outConfig) { if (dataSource == item.config.source) return; item.config.source = dataSource; @@ -2259,14 +2515,102 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntMultiC "Values types do not match: " + std::to_string(dataSource), &item); return; } - outConfig.value = *static_cast>*>(srcItem->getValue()); + outConfig.value = *static_cast>*>(srcItem->getValue()); } void -DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvListboxConfig& outConfig) +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderDoubleConfig& outConfig) { - if (dataSource == item.config.source) return; - item.config.source = dataSource; + if (dataSource == item.config.source) return; + item.config.source = dataSource; + + mvAppItem* srcItem = GetItem((*GContext->itemRegistry), dataSource); + if (!srcItem) + { + mvThrowPythonError(mvErrorCode::mvSourceNotFound, "set_value", + "Source item not found: " + std::to_string(dataSource), &item); + return; + } + if (DearPyGui::GetEntityValueType(srcItem->type) != DearPyGui::GetEntityValueType(item.type)) + { + mvThrowPythonError(mvErrorCode::mvSourceNotCompatible, "set_value", + "Values types do not match: " + std::to_string(dataSource), &item); + return; + } + outConfig.value = *static_cast*>(srcItem->getValue()); +} + +void +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderDoubleMultiConfig& outConfig) +{ + if (dataSource == item.config.source) return; + item.config.source = dataSource; + + mvAppItem* srcItem = GetItem((*GContext->itemRegistry), dataSource); + if (!srcItem) + { + mvThrowPythonError(mvErrorCode::mvSourceNotFound, "set_value", + "Source item not found: " + std::to_string(dataSource), &item); + return; + } + if (DearPyGui::GetEntityValueType(srcItem->type) != DearPyGui::GetEntityValueType(item.type)) + { + mvThrowPythonError(mvErrorCode::mvSourceNotCompatible, "set_value", + "Values types do not match: " + std::to_string(dataSource), &item); + return; + } + outConfig.value = *static_cast>*>(srcItem->getValue()); +} + +void +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntConfig& outConfig) +{ + if (dataSource == item.config.source) return; + item.config.source = dataSource; + + mvAppItem* srcItem = GetItem((*GContext->itemRegistry), dataSource); + if (!srcItem) + { + mvThrowPythonError(mvErrorCode::mvSourceNotFound, "set_value", + "Source item not found: " + std::to_string(dataSource), &item); + return; + } + if (DearPyGui::GetEntityValueType(srcItem->type) != DearPyGui::GetEntityValueType(item.type)) + { + mvThrowPythonError(mvErrorCode::mvSourceNotCompatible, "set_value", + "Values types do not match: " + std::to_string(dataSource), &item); + return; + } + outConfig.value = *static_cast*>(srcItem->getValue()); +} + +void +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntMultiConfig& outConfig) +{ + if (dataSource == item.config.source) return; + item.config.source = dataSource; + + mvAppItem* srcItem = GetItem((*GContext->itemRegistry), dataSource); + if (!srcItem) + { + mvThrowPythonError(mvErrorCode::mvSourceNotFound, "set_value", + "Source item not found: " + std::to_string(dataSource), &item); + return; + } + if (DearPyGui::GetEntityValueType(srcItem->type) != DearPyGui::GetEntityValueType(item.type)) + { + mvThrowPythonError(mvErrorCode::mvSourceNotCompatible, "set_value", + "Values types do not match: " + std::to_string(dataSource), &item); + return; + } + outConfig.value = *static_cast>*>(srcItem->getValue()); +} + +void +DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvListboxConfig& outConfig) +{ + if (dataSource == item.config.source) return; + item.config.source = dataSource; mvAppItem* srcItem = GetItem((*GContext->itemRegistry), dataSource); if (!srcItem) @@ -2867,10 +3211,378 @@ DearPyGui::draw_combo(ImDrawList* drawlist, mvAppItem& item, mvComboConfig& conf ImGui::SetItemDefaultFocus(); } - ImGui::EndCombo(); + ImGui::EndCombo(); + } + } + + //----------------------------------------------------------------------------- + // post draw + //----------------------------------------------------------------------------- + + // set cursor position to cached position + if (item.info.dirtyPos) + ImGui::SetCursorPos(previousCursorPos); + + if (item.config.indent > 0.0f) + ImGui::Unindent(item.config.indent); + + // pop font off stack + if (item.font) + ImGui::PopFont(); + + // handle popping themes + cleanup_local_theming(&item); + + if (item.handlerRegistry) + item.handlerRegistry->checkEvents(&item.state); + + // handle drag & drop if used + apply_drag_drop(&item); +} + +void +DearPyGui::draw_checkbox(ImDrawList* drawlist, mvAppItem& item, mvCheckboxConfig& config) +{ + //----------------------------------------------------------------------------- + // pre draw + //----------------------------------------------------------------------------- + + // show/hide + if (!item.config.show) + return; + + // focusing + if (item.info.focusNextFrame) + { + ImGui::SetKeyboardFocusHere(); + item.info.focusNextFrame = false; + } + + // cache old cursor position + ImVec2 previousCursorPos = ImGui::GetCursorPos(); + + // set cursor position if user set + if (item.info.dirtyPos) + ImGui::SetCursorPos(item.state.pos); + + // update widget's position state + item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; + + // set item width + if (item.config.width != 0) + ImGui::SetNextItemWidth((float)item.config.width); + + // set indent + if (item.config.indent > 0.0f) + ImGui::Indent(item.config.indent); + + // push font if a font object is attached + if (item.font) + { + ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); + ImGui::PushFont(fontptr); + } + + apply_local_theming(&item); + + //----------------------------------------------------------------------------- + // draw + //----------------------------------------------------------------------------- + { + // push imgui id to prevent name collisions + ScopedID id(item.uuid); + + if (!item.config.enabled) config.disabled_value = *config.value; + + if (ImGui::Checkbox(item.info.internalLabel.c_str(), item.config.enabled ? config.value.get() : &config.disabled_value)) + { + item.submitCallback(*config.value); + } + } + + //----------------------------------------------------------------------------- + // update state + //----------------------------------------------------------------------------- + UpdateAppItemState(item.state); + + //----------------------------------------------------------------------------- + // post draw + //----------------------------------------------------------------------------- + + // set cursor position to cached position + if (item.info.dirtyPos) + ImGui::SetCursorPos(previousCursorPos); + + if (item.config.indent > 0.0f) + ImGui::Unindent(item.config.indent); + + // pop font off stack + if (item.font) + ImGui::PopFont(); + + cleanup_local_theming(&item); + + if (item.handlerRegistry) + item.handlerRegistry->checkEvents(&item.state); + + // handle drag & drop if used + apply_drag_drop(&item); +} + +void +DearPyGui::draw_drag_float(ImDrawList* drawlist, mvAppItem& item, mvDragFloatConfig& config) +{ + ScopedID id(item.uuid); + + //----------------------------------------------------------------------------- + // pre draw + //----------------------------------------------------------------------------- + + // show/hide + if (!item.config.show) + return; + + // focusing + if (item.info.focusNextFrame) + { + ImGui::SetKeyboardFocusHere(); + item.info.focusNextFrame = false; + } + + // cache old cursor position + ImVec2 previousCursorPos = ImGui::GetCursorPos(); + + // set cursor position if user set + if (item.info.dirtyPos) + ImGui::SetCursorPos(item.state.pos); + + // update widget's position state + item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; + + // set item width + if (item.config.width != 0) + ImGui::SetNextItemWidth((float)item.config.width); + + // set indent + if (item.config.indent > 0.0f) + ImGui::Indent(item.config.indent); + + // push font if a font object is attached + if (item.font) + { + ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); + ImGui::PushFont(fontptr); + } + + // themes + apply_local_theming(&item); + + //----------------------------------------------------------------------------- + // draw + //----------------------------------------------------------------------------- + { + + if (!item.config.enabled) config.disabled_value = *config.value; + + if (ImGui::DragFloat(item.info.internalLabel.c_str(), + item.config.enabled ? config.value.get() : &config.disabled_value, + config.speed, config.minv, config.maxv, config.format.c_str(), config.flags)) + { + item.submitCallback(*config.value); + } + } + + //----------------------------------------------------------------------------- + // update state + //----------------------------------------------------------------------------- + UpdateAppItemState(item.state); + + //----------------------------------------------------------------------------- + // post draw + //----------------------------------------------------------------------------- + + // set cursor position to cached position + if (item.info.dirtyPos) + ImGui::SetCursorPos(previousCursorPos); + + if (item.config.indent > 0.0f) + ImGui::Unindent(item.config.indent); + + // pop font off stack + if (item.font) + ImGui::PopFont(); + + // handle popping themes + cleanup_local_theming(&item); + + if (item.handlerRegistry) + item.handlerRegistry->checkEvents(&item.state); + + // handle drag & drop if used + apply_drag_drop(&item); +} + +void +DearPyGui::draw_drag_double(ImDrawList* drawlist, mvAppItem& item, mvDragDoubleConfig& config) +{ + ScopedID id(item.uuid); + + //----------------------------------------------------------------------------- + // pre draw + //----------------------------------------------------------------------------- + + // show/hide + if (!item.config.show) + return; + + // focusing + if (item.info.focusNextFrame) + { + ImGui::SetKeyboardFocusHere(); + item.info.focusNextFrame = false; + } + + // cache old cursor position + ImVec2 previousCursorPos = ImGui::GetCursorPos(); + + // set cursor position if user set + if (item.info.dirtyPos) + ImGui::SetCursorPos(item.state.pos); + + // update widget's position state + item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; + + // set item width + if (item.config.width != 0) + ImGui::SetNextItemWidth((float)item.config.width); + + // set indent + if (item.config.indent > 0.0f) + ImGui::Indent(item.config.indent); + + // push font if a font object is attached + if (item.font) + { + ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); + ImGui::PushFont(fontptr); + } + + // themes + apply_local_theming(&item); + + //----------------------------------------------------------------------------- + // draw + //----------------------------------------------------------------------------- + { + + if (!item.config.enabled) config.disabled_value = *config.value; + + if (ImGui::DragScalar(item.info.internalLabel.c_str(), ImGuiDataType_Double, + item.config.enabled ? config.value.get() : &config.disabled_value, + config.speed, &config.minv, &config.maxv, config.format.c_str(), config.flags)) + { + item.submitCallback(*config.value); + } + } + + //----------------------------------------------------------------------------- + // update state + //----------------------------------------------------------------------------- + UpdateAppItemState(item.state); + + //----------------------------------------------------------------------------- + // post draw + //----------------------------------------------------------------------------- + + // set cursor position to cached position + if (item.info.dirtyPos) + ImGui::SetCursorPos(previousCursorPos); + + if (item.config.indent > 0.0f) + ImGui::Unindent(item.config.indent); + + // pop font off stack + if (item.font) + ImGui::PopFont(); + + // handle popping themes + cleanup_local_theming(&item); + + if (item.handlerRegistry) + item.handlerRegistry->checkEvents(&item.state); + + // handle drag & drop if used + apply_drag_drop(&item); +} + +void +DearPyGui::draw_drag_int(ImDrawList* drawlist, mvAppItem& item, mvDragIntConfig& config) +{ + //----------------------------------------------------------------------------- + // pre draw + //----------------------------------------------------------------------------- + + // show/hide + if (!item.config.show) + return; + + // focusing + if (item.info.focusNextFrame) + { + ImGui::SetKeyboardFocusHere(); + item.info.focusNextFrame = false; + } + + // cache old cursor position + ImVec2 previousCursorPos = ImGui::GetCursorPos(); + + // set cursor position if user set + if (item.info.dirtyPos) + ImGui::SetCursorPos(item.state.pos); + + // update widget's position state + item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; + + // set item width + if (item.config.width != 0) + ImGui::SetNextItemWidth((float)item.config.width); + + // set indent + if (item.config.indent > 0.0f) + ImGui::Indent(item.config.indent); + + // push font if a font object is attached + if (item.font) + { + ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); + ImGui::PushFont(fontptr); + } + + // themes + apply_local_theming(&item); + + //----------------------------------------------------------------------------- + // draw + //----------------------------------------------------------------------------- + { + + ScopedID id(item.uuid); + + if (!item.config.enabled) config.disabled_value = *config.value; + + if (ImGui::DragInt(item.info.internalLabel.c_str(), + item.config.enabled ? config.value.get() : &config.disabled_value, config.speed, + config.minv, config.maxv, config.format.c_str(), config.flags)) + { + item.submitCallback(*config.value); } } + //----------------------------------------------------------------------------- + // update state + //----------------------------------------------------------------------------- + UpdateAppItemState(item.state); + //----------------------------------------------------------------------------- // post draw //----------------------------------------------------------------------------- @@ -2897,13 +3609,13 @@ DearPyGui::draw_combo(ImDrawList* drawlist, mvAppItem& item, mvComboConfig& conf } void -DearPyGui::draw_checkbox(ImDrawList* drawlist, mvAppItem& item, mvCheckboxConfig& config) +DearPyGui::draw_drag_intx(ImDrawList* drawlist, mvAppItem& item, mvDragIntMultiConfig& config) { //----------------------------------------------------------------------------- - // pre draw - //----------------------------------------------------------------------------- +// pre draw +//----------------------------------------------------------------------------- - // show/hide +// show/hide if (!item.config.show) return; @@ -2939,18 +3651,37 @@ DearPyGui::draw_checkbox(ImDrawList* drawlist, mvAppItem& item, mvCheckboxConfig ImGui::PushFont(fontptr); } + // themes apply_local_theming(&item); + //----------------------------------------------------------------------------- // draw //----------------------------------------------------------------------------- { - // push imgui id to prevent name collisions + ScopedID id(item.uuid); - if (!item.config.enabled) config.disabled_value = *config.value; + bool activated = false; - if (ImGui::Checkbox(item.info.internalLabel.c_str(), item.config.enabled ? config.value.get() : &config.disabled_value)) + if (!item.config.enabled) std::copy(config.value->data(), config.value->data() + 2, config.disabled_value); + + switch (config.size) + { + case 2: + activated = ImGui::DragInt2(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.speed, config.minv, config.maxv, config.format.c_str(), config.flags); + break; + case 3: + activated = ImGui::DragInt3(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.speed, config.minv, config.maxv, config.format.c_str(), config.flags); + break; + case 4: + activated = ImGui::DragInt4(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.speed, config.minv, config.maxv, config.format.c_str(), config.flags); + break; + default: + break; + } + + if (activated) { item.submitCallback(*config.value); } @@ -2976,6 +3707,7 @@ DearPyGui::draw_checkbox(ImDrawList* drawlist, mvAppItem& item, mvCheckboxConfig if (item.font) ImGui::PopFont(); + // handle popping themes cleanup_local_theming(&item); if (item.handlerRegistry) @@ -2986,390 +3718,312 @@ DearPyGui::draw_checkbox(ImDrawList* drawlist, mvAppItem& item, mvCheckboxConfig } void -DearPyGui::draw_drag_float(ImDrawList* drawlist, mvAppItem& item, mvDragFloatConfig& config) +DearPyGui::draw_drag_int_range(ImDrawList* drawlist, mvAppItem& item, mvDragIntRangeConfig& config) { - ScopedID id(item.uuid); - - //----------------------------------------------------------------------------- // pre draw - //----------------------------------------------------------------------------- - - // show/hide if (!item.config.show) return; - // focusing if (item.info.focusNextFrame) { ImGui::SetKeyboardFocusHere(); item.info.focusNextFrame = false; } - // cache old cursor position ImVec2 previousCursorPos = ImGui::GetCursorPos(); - // set cursor position if user set if (item.info.dirtyPos) ImGui::SetCursorPos(item.state.pos); - // update widget's position state item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; - // set item width if (item.config.width != 0) ImGui::SetNextItemWidth((float)item.config.width); - // set indent if (item.config.indent > 0.0f) ImGui::Indent(item.config.indent); - // push font if a font object is attached if (item.font) { ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); ImGui::PushFont(fontptr); } - // themes apply_local_theming(&item); - //----------------------------------------------------------------------------- // draw - //----------------------------------------------------------------------------- { + ScopedID id(item.uuid); - if (!item.config.enabled) config.disabled_value = *config.value; - - if (ImGui::DragFloat(item.info.internalLabel.c_str(), - item.config.enabled ? config.value.get() : &config.disabled_value, - config.speed, config.minv, config.maxv, config.format.c_str(), config.flags)) + if (!item.config.enabled) { - item.submitCallback(*config.value); + config.disabled_value[0] = (*config.value)[0]; + config.disabled_value[1] = (*config.value)[1]; } + + int* value_ptr = item.config.enabled ? config.value->data() : config.disabled_value; + const char* format_max = config.format_max.empty() ? nullptr : config.format_max.c_str(); + + bool activated = ImGui::DragIntRange2( + item.info.internalLabel.c_str(), + &value_ptr[0], &value_ptr[1], + config.speed, config.minv, config.maxv, + config.format.c_str(), format_max, config.flags); + + if (activated) + item.submitCallback(*config.value); } - //----------------------------------------------------------------------------- // update state - //----------------------------------------------------------------------------- UpdateAppItemState(item.state); - //----------------------------------------------------------------------------- // post draw - //----------------------------------------------------------------------------- - - // set cursor position to cached position if (item.info.dirtyPos) ImGui::SetCursorPos(previousCursorPos); if (item.config.indent > 0.0f) ImGui::Unindent(item.config.indent); - // pop font off stack if (item.font) ImGui::PopFont(); - // handle popping themes cleanup_local_theming(&item); if (item.handlerRegistry) item.handlerRegistry->checkEvents(&item.state); - // handle drag & drop if used apply_drag_drop(&item); } void -DearPyGui::draw_drag_double(ImDrawList* drawlist, mvAppItem& item, mvDragDoubleConfig& config) +DearPyGui::draw_drag_float_range(ImDrawList* drawlist, mvAppItem& item, mvDragFloatRangeConfig& config) { - ScopedID id(item.uuid); - - //----------------------------------------------------------------------------- // pre draw - //----------------------------------------------------------------------------- - - // show/hide if (!item.config.show) return; - // focusing if (item.info.focusNextFrame) { ImGui::SetKeyboardFocusHere(); item.info.focusNextFrame = false; } - // cache old cursor position ImVec2 previousCursorPos = ImGui::GetCursorPos(); - // set cursor position if user set if (item.info.dirtyPos) ImGui::SetCursorPos(item.state.pos); - // update widget's position state item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; - // set item width if (item.config.width != 0) ImGui::SetNextItemWidth((float)item.config.width); - // set indent if (item.config.indent > 0.0f) ImGui::Indent(item.config.indent); - // push font if a font object is attached if (item.font) { ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); ImGui::PushFont(fontptr); } - // themes apply_local_theming(&item); - //----------------------------------------------------------------------------- // draw - //----------------------------------------------------------------------------- { + ScopedID id(item.uuid); - if (!item.config.enabled) config.disabled_value = *config.value; - - if (ImGui::DragScalar(item.info.internalLabel.c_str(), ImGuiDataType_Double, - item.config.enabled ? config.value.get() : &config.disabled_value, - config.speed, &config.minv, &config.maxv, config.format.c_str(), config.flags)) + if (!item.config.enabled) { - item.submitCallback(*config.value); + config.disabled_value[0] = (*config.value)[0]; + config.disabled_value[1] = (*config.value)[1]; } + + float* value_ptr = item.config.enabled ? config.value->data() : config.disabled_value; + const char* format_max = config.format_max.empty() ? nullptr : config.format_max.c_str(); + + bool activated = ImGui::DragFloatRange2( + item.info.internalLabel.c_str(), + &value_ptr[0], &value_ptr[1], + config.speed, config.minv, config.maxv, + config.format.c_str(), format_max, config.flags); + + if (activated) + item.submitCallback(*config.value); } - //----------------------------------------------------------------------------- // update state - //----------------------------------------------------------------------------- UpdateAppItemState(item.state); - //----------------------------------------------------------------------------- // post draw - //----------------------------------------------------------------------------- - - // set cursor position to cached position if (item.info.dirtyPos) ImGui::SetCursorPos(previousCursorPos); if (item.config.indent > 0.0f) ImGui::Unindent(item.config.indent); - // pop font off stack if (item.font) ImGui::PopFont(); - // handle popping themes cleanup_local_theming(&item); if (item.handlerRegistry) item.handlerRegistry->checkEvents(&item.state); - // handle drag & drop if used apply_drag_drop(&item); } void -DearPyGui::draw_drag_int(ImDrawList* drawlist, mvAppItem& item, mvDragIntConfig& config) +DearPyGui::draw_slider_int_range(ImDrawList* drawlist, mvAppItem& item, mvSliderIntRangeConfig& config) { - //----------------------------------------------------------------------------- // pre draw - //----------------------------------------------------------------------------- - - // show/hide if (!item.config.show) return; - // focusing if (item.info.focusNextFrame) { ImGui::SetKeyboardFocusHere(); item.info.focusNextFrame = false; } - // cache old cursor position ImVec2 previousCursorPos = ImGui::GetCursorPos(); - // set cursor position if user set if (item.info.dirtyPos) ImGui::SetCursorPos(item.state.pos); - // update widget's position state item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; - // set item width if (item.config.width != 0) ImGui::SetNextItemWidth((float)item.config.width); - // set indent if (item.config.indent > 0.0f) ImGui::Indent(item.config.indent); - // push font if a font object is attached if (item.font) { ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); ImGui::PushFont(fontptr); } - // themes apply_local_theming(&item); - //----------------------------------------------------------------------------- // draw - //----------------------------------------------------------------------------- { - ScopedID id(item.uuid); - if (!item.config.enabled) config.disabled_value = *config.value; - - if (ImGui::DragInt(item.info.internalLabel.c_str(), - item.config.enabled ? config.value.get() : &config.disabled_value, config.speed, - config.minv, config.maxv, config.format.c_str(), config.flags)) + if (!item.config.enabled) { - item.submitCallback(*config.value); + config.disabled_value[0] = (*config.value)[0]; + config.disabled_value[1] = (*config.value)[1]; } + + int* value_ptr = item.config.enabled ? config.value->data() : config.disabled_value; + + bool activated = ImGui::SliderIntRange2( + item.info.internalLabel.c_str(), + &value_ptr[0], &value_ptr[1], + config.minv, config.maxv, + config.format.c_str(), config.flags, config.step); + + if (activated) + item.submitCallback(*config.value); } - //----------------------------------------------------------------------------- // update state - //----------------------------------------------------------------------------- UpdateAppItemState(item.state); - //----------------------------------------------------------------------------- // post draw - //----------------------------------------------------------------------------- - - // set cursor position to cached position if (item.info.dirtyPos) ImGui::SetCursorPos(previousCursorPos); if (item.config.indent > 0.0f) ImGui::Unindent(item.config.indent); - // pop font off stack if (item.font) ImGui::PopFont(); - // handle popping themes cleanup_local_theming(&item); if (item.handlerRegistry) item.handlerRegistry->checkEvents(&item.state); - // handle drag & drop if used apply_drag_drop(&item); } void -DearPyGui::draw_drag_intx(ImDrawList* drawlist, mvAppItem& item, mvDragIntMultiConfig& config) +DearPyGui::draw_slider_float_range(ImDrawList* drawlist, mvAppItem& item, mvSliderFloatRangeConfig& config) { - //----------------------------------------------------------------------------- -// pre draw -//----------------------------------------------------------------------------- - -// show/hide + // pre draw if (!item.config.show) return; - // focusing if (item.info.focusNextFrame) { ImGui::SetKeyboardFocusHere(); item.info.focusNextFrame = false; } - // cache old cursor position ImVec2 previousCursorPos = ImGui::GetCursorPos(); - // set cursor position if user set if (item.info.dirtyPos) ImGui::SetCursorPos(item.state.pos); - // update widget's position state item.state.pos = { ImGui::GetCursorPosX(), ImGui::GetCursorPosY() }; - // set item width if (item.config.width != 0) ImGui::SetNextItemWidth((float)item.config.width); - // set indent if (item.config.indent > 0.0f) ImGui::Indent(item.config.indent); - // push font if a font object is attached if (item.font) { ImFont* fontptr = static_cast(item.font.get())->getFontPtr(); ImGui::PushFont(fontptr); } - // themes apply_local_theming(&item); - - //----------------------------------------------------------------------------- // draw - //----------------------------------------------------------------------------- { - ScopedID id(item.uuid); - bool activated = false; - - if (!item.config.enabled) std::copy(config.value->data(), config.value->data() + 2, config.disabled_value); - - switch (config.size) + if (!item.config.enabled) { - case 2: - activated = ImGui::DragInt2(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.speed, config.minv, config.maxv, config.format.c_str(), config.flags); - break; - case 3: - activated = ImGui::DragInt3(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.speed, config.minv, config.maxv, config.format.c_str(), config.flags); - break; - case 4: - activated = ImGui::DragInt4(item.info.internalLabel.c_str(), item.config.enabled ? config.value->data() : &config.disabled_value[0], config.speed, config.minv, config.maxv, config.format.c_str(), config.flags); - break; - default: - break; + config.disabled_value[0] = (*config.value)[0]; + config.disabled_value[1] = (*config.value)[1]; } + float* value_ptr = item.config.enabled ? config.value->data() : config.disabled_value; + + bool activated = ImGui::SliderFloatRange2( + item.info.internalLabel.c_str(), + &value_ptr[0], &value_ptr[1], + config.minv, config.maxv, + config.format.c_str(), config.flags, config.step); + if (activated) - { item.submitCallback(*config.value); - } } - //----------------------------------------------------------------------------- // update state - //----------------------------------------------------------------------------- UpdateAppItemState(item.state); - //----------------------------------------------------------------------------- // post draw - //----------------------------------------------------------------------------- - - // set cursor position to cached position if (item.info.dirtyPos) ImGui::SetCursorPos(previousCursorPos); if (item.config.indent > 0.0f) ImGui::Unindent(item.config.indent); - // pop font off stack if (item.font) ImGui::PopFont(); - // handle popping themes cleanup_local_theming(&item); if (item.handlerRegistry) item.handlerRegistry->checkEvents(&item.state); - // handle drag & drop if used apply_drag_drop(&item); } @@ -5927,7 +6581,7 @@ DearPyGui::draw_image(ImDrawList* drawlist, mvAppItem& item, mvImageConfig& conf else texture = static_cast(config.texture.get())->_texture; - ImGui::Image(texture, ImVec2((float)item.config.width, (float)item.config.height), ImVec2(config.uv_min.x, config.uv_min.y), ImVec2(config.uv_max.x, config.uv_max.y), + ImGui::Image((ImTextureID)(size_t)texture, ImVec2((float)item.config.width, (float)item.config.height), ImVec2(config.uv_min.x, config.uv_min.y), ImVec2(config.uv_max.x, config.uv_max.y), ImVec4((float)config.tintColor.r, (float)config.tintColor.g, (float)config.tintColor.b, (float)config.tintColor.a), ImVec4((float)config.borderColor.r, (float)config.borderColor.g, (float)config.borderColor.b, (float)config.borderColor.a)); @@ -6045,7 +6699,7 @@ DearPyGui::draw_image_button(ImDrawList* drawlist, mvAppItem& item, mvImageButto ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)config.framePadding, (float)config.framePadding)); ImGui::PushID(item.uuid); - if (ImGui::ImageButton(item.info.internalLabel.c_str(), texture, ImVec2((float)item.config.width, (float)item.config.height), + if (ImGui::ImageButton(item.info.internalLabel.c_str(), (ImTextureID)(size_t)texture, ImVec2((float)item.config.width, (float)item.config.height), ImVec2(config.uv_min.x, config.uv_min.y), ImVec2(config.uv_max.x, config.uv_max.y), config.backgroundColor, config.tintColor)) { @@ -6277,6 +6931,66 @@ mvDragIntMulti::setPyValue(PyObject* value) configData.value = std::make_shared>(temp_array); } +void +mvDragIntRange::setPyValue(PyObject* value) +{ + std::vector temp = ToIntVect(value); + while (temp.size() < 2) + temp.push_back(0); + std::array temp_array; + for (size_t i = 0; i < temp_array.size(); i++) + temp_array[i] = temp[i]; + if (configData.value) + *configData.value = temp_array; + else + configData.value = std::make_shared>(temp_array); +} + +void +mvDragFloatRange::setPyValue(PyObject* value) +{ + std::vector temp = ToFloatVect(value); + while (temp.size() < 2) + temp.push_back(0.0f); + std::array temp_array; + for (size_t i = 0; i < temp_array.size(); i++) + temp_array[i] = temp[i]; + if (configData.value) + *configData.value = temp_array; + else + configData.value = std::make_shared>(temp_array); +} + +void +mvSliderIntRange::setPyValue(PyObject* value) +{ + std::vector temp = ToIntVect(value); + while (temp.size() < 2) + temp.push_back(0); + std::array temp_array; + for (size_t i = 0; i < temp_array.size(); i++) + temp_array[i] = temp[i]; + if (configData.value) + *configData.value = temp_array; + else + configData.value = std::make_shared>(temp_array); +} + +void +mvSliderFloatRange::setPyValue(PyObject* value) +{ + std::vector temp = ToFloatVect(value); + while (temp.size() < 2) + temp.push_back(0.0f); + std::array temp_array; + for (size_t i = 0; i < temp_array.size(); i++) + temp_array[i] = temp[i]; + if (configData.value) + *configData.value = temp_array; + else + configData.value = std::make_shared>(temp_array); +} + void mvDragFloatMulti::setPyValue(PyObject* value) { diff --git a/src/mvBasicWidgets.h b/src/mvBasicWidgets.h index 576d14239..c504fe697 100644 --- a/src/mvBasicWidgets.h +++ b/src/mvBasicWidgets.h @@ -13,6 +13,10 @@ struct mvDragDoubleConfig; struct mvDragFloatMultiConfig; struct mvDragDoubleMultiConfig; struct mvDragIntMultiConfig; +struct mvDragIntRangeConfig; +struct mvDragFloatRangeConfig; +struct mvSliderIntRangeConfig; +struct mvSliderFloatRangeConfig; struct mvSliderIntConfig; struct mvSliderFloatConfig; struct mvSliderDoubleConfig; @@ -51,6 +55,10 @@ namespace DearPyGui void fill_configuration_dict(const mvDragDoubleMultiConfig& inConfig, PyObject* outDict); void fill_configuration_dict(const mvDragIntConfig& inConfig, PyObject* outDict); void fill_configuration_dict(const mvDragIntMultiConfig& inConfig, PyObject* outDict); + void fill_configuration_dict(const mvDragIntRangeConfig& inConfig, PyObject* outDict); + void fill_configuration_dict(const mvDragFloatRangeConfig& inConfig, PyObject* outDict); + void fill_configuration_dict(const mvSliderIntRangeConfig& inConfig, PyObject* outDict); + void fill_configuration_dict(const mvSliderFloatRangeConfig& inConfig, PyObject* outDict); void fill_configuration_dict(const mvSliderIntConfig& inConfig, PyObject* outDict); void fill_configuration_dict(const mvSliderIntMultiConfig& inConfig, PyObject* outDict); void fill_configuration_dict(const mvSliderFloatConfig& inConfig, PyObject* outDict); @@ -86,6 +94,10 @@ namespace DearPyGui void set_configuration(PyObject* inDict, mvDragDoubleMultiConfig& outConfig, mvAppItemInfo& info); void set_configuration(PyObject* inDict, mvDragFloatMultiConfig& outConfig, mvAppItemInfo& info); void set_configuration(PyObject* inDict, mvDragIntMultiConfig& outConfig, mvAppItemInfo& info); + void set_configuration(PyObject* inDict, mvDragIntRangeConfig& outConfig, mvAppItemInfo& info); + void set_configuration(PyObject* inDict, mvDragFloatRangeConfig& outConfig, mvAppItemInfo& info); + void set_configuration(PyObject* inDict, mvSliderIntRangeConfig& outConfig, mvAppItemInfo& info); + void set_configuration(PyObject* inDict, mvSliderFloatRangeConfig& outConfig, mvAppItemInfo& info); void set_configuration(PyObject* inDict, mvSliderIntConfig& outConfig, mvAppItemInfo& info); void set_configuration(PyObject* inDict, mvSliderIntMultiConfig& outConfig, mvAppItemInfo& info); void set_configuration(PyObject* inDict, mvSliderFloatConfig& outConfig, mvAppItemInfo& info); @@ -132,6 +144,10 @@ namespace DearPyGui void set_data_source(mvAppItem& item, mvUUID dataSource, mvDragIntMultiConfig& outConfig); void set_data_source(mvAppItem& item, mvUUID dataSource, mvDragDoubleConfig& outConfig); void set_data_source(mvAppItem& item, mvUUID dataSource, mvDragDoubleMultiConfig& outConfig); + void set_data_source(mvAppItem& item, mvUUID dataSource, mvDragIntRangeConfig& outConfig); + void set_data_source(mvAppItem& item, mvUUID dataSource, mvDragFloatRangeConfig& outConfig); + void set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderIntRangeConfig& outConfig); + void set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatRangeConfig& outConfig); void set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatConfig& outConfig); void set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderFloatMultiConfig& outConfig); void set_data_source(mvAppItem& item, mvUUID dataSource, mvSliderDoubleConfig& outConfig); @@ -164,6 +180,10 @@ namespace DearPyGui void draw_drag_doublex (ImDrawList* drawlist, mvAppItem& item, mvDragDoubleMultiConfig& config); void draw_drag_int (ImDrawList* drawlist, mvAppItem& item, mvDragIntConfig& config); void draw_drag_intx (ImDrawList* drawlist, mvAppItem& item, mvDragIntMultiConfig& config); + void draw_drag_int_range(ImDrawList* drawlist, mvAppItem& item, mvDragIntRangeConfig& config); + void draw_drag_float_range(ImDrawList* drawlist, mvAppItem& item, mvDragFloatRangeConfig& config); + void draw_slider_int_range(ImDrawList* drawlist, mvAppItem& item, mvSliderIntRangeConfig& config); + void draw_slider_float_range(ImDrawList* drawlist, mvAppItem& item, mvSliderFloatRangeConfig& config); void draw_slider_float (ImDrawList* drawlist, mvAppItem& item, mvSliderFloatConfig& config); void draw_slider_floatx(ImDrawList* drawlist, mvAppItem& item, mvSliderFloatMultiConfig& config); void draw_slider_double(ImDrawList* drawlist, mvAppItem& item, mvSliderDoubleConfig& config); @@ -314,6 +334,58 @@ struct mvDragDoubleMultiConfig double disabled_value[4]{}; }; +struct mvDragIntRangeConfig +{ + float speed = 1.0f; + int minv = 0; + int maxv = 100; + std::string format = "%d"; + std::string format_max = ""; + ImGuiInputTextFlags flags = ImGuiSliderFlags_None; + ImGuiInputTextFlags stor_flags = ImGuiSliderFlags_None; + std::shared_ptr> value = std::make_shared>(std::array{0, 100}); + int disabled_value[2]{}; +}; + +struct mvDragFloatRangeConfig +{ + float speed = 1.0f; + float minv = 0.0f; + float maxv = 100.0f; + std::string format = "%.3f"; + std::string format_max = ""; + ImGuiInputTextFlags flags = ImGuiSliderFlags_None; + ImGuiInputTextFlags stor_flags = ImGuiSliderFlags_None; + std::shared_ptr> value = std::make_shared>(std::array{0.0f, 100.0f}); + float disabled_value[2]{}; +}; + +struct mvSliderIntRangeConfig +{ + int minv = 0; + int maxv = 100; + int step = 0; + std::string format = "%d"; + std::string format_max = ""; + ImGuiInputTextFlags flags = ImGuiSliderFlags_None; + ImGuiInputTextFlags stor_flags = ImGuiSliderFlags_None; + std::shared_ptr> value = std::make_shared>(std::array{0, 100}); + int disabled_value[2]{}; +}; + +struct mvSliderFloatRangeConfig +{ + float minv = 0.0f; + float maxv = 100.0f; + float step = 0.0f; + std::string format = "%.3f"; + std::string format_max = ""; + ImGuiInputTextFlags flags = ImGuiSliderFlags_None; + ImGuiInputTextFlags stor_flags = ImGuiSliderFlags_None; + std::shared_ptr> value = std::make_shared>(std::array{0.0f, 100.0f}); + float disabled_value[2]{}; +}; + struct mvSliderIntConfig { int minv = 0; @@ -704,6 +776,62 @@ class mvDragIntMulti : public mvAppItem void setPyValue(PyObject* value) override; }; +class mvDragIntRange : public mvAppItem +{ +public: + mvDragIntRangeConfig configData{}; + explicit mvDragIntRange(mvUUID uuid) : mvAppItem(uuid) {} + void draw(ImDrawList* drawlist, float x, float y) override { DearPyGui::draw_drag_int_range(drawlist, *this, configData); } + void handleSpecificKeywordArgs(PyObject* dict) override { DearPyGui::set_configuration(dict, configData, info); } + void getSpecificConfiguration(PyObject* dict) override { DearPyGui::fill_configuration_dict(configData, dict); } + void setDataSource(mvUUID dataSource) override { DearPyGui::set_data_source(*this, dataSource, configData); } + void* getValue() override { return &configData.value; } + PyObject* getPyValue() override { return ToPyIntList(configData.value->data(), 2); } + void setPyValue(PyObject* value) override; +}; + +class mvDragFloatRange : public mvAppItem +{ +public: + mvDragFloatRangeConfig configData{}; + explicit mvDragFloatRange(mvUUID uuid) : mvAppItem(uuid) {} + void draw(ImDrawList* drawlist, float x, float y) override { DearPyGui::draw_drag_float_range(drawlist, *this, configData); } + void handleSpecificKeywordArgs(PyObject* dict) override { DearPyGui::set_configuration(dict, configData, info); } + void getSpecificConfiguration(PyObject* dict) override { DearPyGui::fill_configuration_dict(configData, dict); } + void setDataSource(mvUUID dataSource) override { DearPyGui::set_data_source(*this, dataSource, configData); } + void* getValue() override { return &configData.value; } + PyObject* getPyValue() override { return ToPyFloatList(configData.value->data(), 2); } + void setPyValue(PyObject* value) override; +}; + +class mvSliderIntRange : public mvAppItem +{ +public: + mvSliderIntRangeConfig configData{}; + explicit mvSliderIntRange(mvUUID uuid) : mvAppItem(uuid) {} + void draw(ImDrawList* drawlist, float x, float y) override { DearPyGui::draw_slider_int_range(drawlist, *this, configData); } + void handleSpecificKeywordArgs(PyObject* dict) override { DearPyGui::set_configuration(dict, configData, info); } + void getSpecificConfiguration(PyObject* dict) override { DearPyGui::fill_configuration_dict(configData, dict); } + void setDataSource(mvUUID dataSource) override { DearPyGui::set_data_source(*this, dataSource, configData); } + void* getValue() override { return &configData.value; } + PyObject* getPyValue() override { return ToPyIntList(configData.value->data(), 2); } + void setPyValue(PyObject* value) override; +}; + +class mvSliderFloatRange : public mvAppItem +{ +public: + mvSliderFloatRangeConfig configData{}; + explicit mvSliderFloatRange(mvUUID uuid) : mvAppItem(uuid) {} + void draw(ImDrawList* drawlist, float x, float y) override { DearPyGui::draw_slider_float_range(drawlist, *this, configData); } + void handleSpecificKeywordArgs(PyObject* dict) override { DearPyGui::set_configuration(dict, configData, info); } + void getSpecificConfiguration(PyObject* dict) override { DearPyGui::fill_configuration_dict(configData, dict); } + void setDataSource(mvUUID dataSource) override { DearPyGui::set_data_source(*this, dataSource, configData); } + void* getValue() override { return &configData.value; } + PyObject* getPyValue() override { return ToPyFloatList(configData.value->data(), 2); } + void setPyValue(PyObject* value) override; +}; + class mvDragFloatMulti : public mvAppItem { public: diff --git a/src/mvContainers.cpp b/src/mvContainers.cpp index 367c9f39c..0c2038550 100644 --- a/src/mvContainers.cpp +++ b/src/mvContainers.cpp @@ -68,10 +68,9 @@ 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); - // child flags - checkbitset("border", ImGuiChildFlags_Border, inConfig.childFlags); + checkbitset("flattened_navigation", ImGuiChildFlags_NavFlattened, inConfig.childFlags); + checkbitset("border", ImGuiChildFlags_Borders, inConfig.childFlags); checkbitset("always_auto_resize", ImGuiChildFlags_AlwaysAutoResize, inConfig.childFlags); checkbitset("always_use_window_padding", ImGuiChildFlags_AlwaysUseWindowPadding, inConfig.childFlags); checkbitset("auto_resize_x", ImGuiChildFlags_AutoResizeX, inConfig.childFlags); @@ -274,10 +273,9 @@ 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); - // child flags - flagop("border", ImGuiChildFlags_Border, outConfig.childFlags); + flagop("flattened_navigation", ImGuiChildFlags_NavFlattened, outConfig.childFlags); + flagop("border", ImGuiChildFlags_Borders, outConfig.childFlags); flagop("always_auto_resize", ImGuiChildFlags_AlwaysAutoResize, outConfig.childFlags); flagop("always_use_window_padding", ImGuiChildFlags_AlwaysUseWindowPadding, outConfig.childFlags); flagop("auto_resize_x", ImGuiChildFlags_AutoResizeX, outConfig.childFlags); diff --git a/src/mvContainers.h b/src/mvContainers.h index a5390c7fc..791f644d4 100644 --- a/src/mvContainers.h +++ b/src/mvContainers.h @@ -97,10 +97,10 @@ enum class TabOrdering { struct mvChildWindowConfig { - ImGuiChildFlags childFlags = ImGuiChildFlags_Border; + ImGuiChildFlags childFlags = ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened; bool autosize_x = false; bool autosize_y = false; - ImGuiWindowFlags windowflags = ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NavFlattened; + ImGuiWindowFlags windowflags = ImGuiWindowFlags_NoSavedSettings; }; struct mvTreeNodeConfig diff --git a/src/mvDebugWindow.cpp b/src/mvDebugWindow.cpp index b666fcf20..740b78208 100644 --- a/src/mvDebugWindow.cpp +++ b/src/mvDebugWindow.cpp @@ -28,7 +28,7 @@ void mvDebugWindow::drawWidgets() filter.Draw(); ImGui::PushItemWidth(-1); - ImGui::BeginChild("CommandsChild##debug", ImVec2(400.0f, 400.0f), ImGuiChildFlags_Border); + ImGui::BeginChild("CommandsChild##debug", ImVec2(400.0f, 400.0f), ImGuiChildFlags_Borders); ImGui::PushStyleColor(ImGuiCol_Text, { 1.0f, 1.0f, 0.0f, 1.0f }); for (size_t i = 0; i < m_commands.size(); i++) { @@ -43,7 +43,7 @@ void mvDebugWindow::drawWidgets() ImGui::PopStyleColor(); ImGui::EndChild(); ImGui::SameLine(); - ImGui::BeginChild("CommandsDoc##debug", ImVec2(-1.0f, 400.0f), ImGuiChildFlags_Border); + ImGui::BeginChild("CommandsDoc##debug", ImVec2(-1.0f, 400.0f), ImGuiChildFlags_Borders); ImGui::PushStyleColor(ImGuiCol_Text, { 1.0f, 0.0f, 1.0f, 1.0f }); ImGui::PushTextWrapPos(500); ImGui::Text("%s", commanddoc); diff --git a/src/mvDocWindow.cpp b/src/mvDocWindow.cpp index 5034d8de2..37a293660 100644 --- a/src/mvDocWindow.cpp +++ b/src/mvDocWindow.cpp @@ -91,7 +91,7 @@ void mvDocWindow::drawWidgets() ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 255, 100)); - ImGui::BeginChild("DocChild", ImVec2(0, 0), ImGuiChildFlags_Border); + ImGui::BeginChild("DocChild", ImVec2(0, 0), ImGuiChildFlags_Borders); ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 400); ImGui::Text("%s", m_doc); ImGui::PopTextWrapPos(); @@ -111,7 +111,7 @@ void mvDocWindow::drawWidgets() ImGui::PushItemWidth(300); ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); - ImGui::BeginChild("CommandsChild##debug", ImVec2(500.0f, 0), ImGuiChildFlags_Border); + ImGui::BeginChild("CommandsChild##debug", ImVec2(500.0f, 0), ImGuiChildFlags_Borders); for (size_t i = 0; i < m_commands.size(); i++) { @@ -127,7 +127,7 @@ void mvDocWindow::drawWidgets() ImGui::EndChild(); ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 255, 100)); - ImGui::BeginChild("CommandsDoc##debug", ImVec2(0, 0), ImGuiChildFlags_Border); + ImGui::BeginChild("CommandsDoc##debug", ImVec2(0, 0), ImGuiChildFlags_Borders); ImGui::PushTextWrapPos(500); ImGui::Text("%s", commanddoc); ImGui::PopStyleColor(); diff --git a/src/mvFontManager.cpp b/src/mvFontManager.cpp index a6e2e6437..20b185923 100644 --- a/src/mvFontManager.cpp +++ b/src/mvFontManager.cpp @@ -40,8 +40,9 @@ NodeFont(ImFont* font) { ImGuiIO& io = ImGui::GetIO(); ImGuiStyle& style = ImGui::GetStyle(); +ImFontBaked* baked = font->GetFontBaked(font->LegacySize); bool font_details_opened = ImGui::TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", - font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); + font->GetDebugName(), font->LegacySize, baked ? baked->Glyphs.Size : 0, (int)font->Sources.Size); ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { io.FontDefault = font; } if (!font_details_opened) return; @@ -49,6 +50,7 @@ if (!font_details_opened) ImGui::PushFont(font); ImGui::Text("The quick brown fox jumps over the lazy dog"); ImGui::PopFont(); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font ImGui::SameLine(); HelpMarker( "Note than the default embedded font is NOT meant to be scaled.\n\n" @@ -56,19 +58,22 @@ ImGui::SameLine(); HelpMarker( "You may oversample them to get some flexibility with scaling. " "You can also render at multiple sizes and select which one to use at runtime.\n\n" "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"); +#endif //ImGui::InputFloat("Font offset", &font->GlyphOffset.y, 1, 1, "%.0f"); -//ImGui::InputInt("Font offset", &font->ConfigData->GlyphOffset.y, 1, 1, "%.0f"); -ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); +if (baked) { + ImGui::Text("Ascent: %f, Descent: %f, Height: %f", baked->Ascent, baked->Descent, baked->Ascent - baked->Descent); +} ImGui::Text("Fallback character: '%c' (U+%04X)", font->FallbackChar, font->FallbackChar); ImGui::Text("Ellipsis character: '%c' (U+%04X)", font->EllipsisChar, font->EllipsisChar); -const int surface_sqrt = (int)sqrtf((float)font->MetricsTotalSurface); -ImGui::Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); -for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) - if (font->ConfigData) - if (const ImFontConfig* cfg = &font->ConfigData[config_i]) - ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", - config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); -if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) +if (baked) { + const int surface_sqrt = (int)sqrtf((float)baked->MetricsTotalSurface); + ImGui::Text("Texture Area: about %d px ~%dx%d px", baked->MetricsTotalSurface, surface_sqrt, surface_sqrt); +} +for (int config_i = 0; config_i < (int)font->Sources.Size; config_i++) + if (const ImFontConfig* cfg = font->Sources[config_i]) + ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", + config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); +if (baked && ImGui::TreeNode("Glyphs", "Glyphs (%d)", baked->Glyphs.Size)) { // Display all glyphs of the fonts in separate pages of 256 characters const ImU32 glyph_col = ImGui::GetColorU32(ImGuiCol_Text); @@ -85,13 +90,13 @@ if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) int count = 0; for (unsigned int n = 0; n < 256; n++) - if (font->FindGlyphNoFallback((ImWchar)(base + n))) + if (baked->FindGlyphNoFallback((ImWchar)(base + n))) count++; if (count <= 0) continue; if (!ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) continue; - float cell_size = font->FontSize * 1; + float cell_size = font->LegacySize * 1; float cell_spacing = style.ItemSpacing.y; ImVec2 base_pos = ImGui::GetCursorScreenPos(); ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -101,7 +106,7 @@ if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + const ImFontGlyph* glyph = baked->FindGlyphNoFallback((ImWchar)(base + n)); draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); if (glyph) font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); @@ -169,9 +174,10 @@ mvFontManager::rebuildAtlas() item->customAction(nullptr); } - // Just to make sure g.Font doesn't point to a font already deleted by + // Just to make sure g.Font doesn't point to a font already deleted by // io.Fonts->Clear(), though ideally ImGui should be doing it on its own. - ImGui::SetCurrentFont(ImGui::GetDefaultFont()); + ImFont* defaultFont = ImGui::GetDefaultFont(); + ImGui::SetCurrentFont(defaultFont, defaultFont->LegacySize, defaultFont->LegacySize); } _dirty = false; @@ -202,11 +208,11 @@ mvFontManager::drawWidgets() NodeFont(font); ImGui::PopID(); } - if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexData->Width, atlas->TexData->Height)) { ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); + ImGui::Image(atlas->TexRef.GetTexID(), ImVec2((float)atlas->TexData->Width, (float)atlas->TexData->Height), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); ImGui::TreePop(); } diff --git a/src/mvPlotting.cpp b/src/mvPlotting.cpp index 6eb507e07..2d118c70c 100644 --- a/src/mvPlotting.cpp +++ b/src/mvPlotting.cpp @@ -592,6 +592,19 @@ DearPyGui::draw_plot(ImDrawList* drawlist, mvAppItem& item, mvPlotConfig& config }); } + // Cache plot geometry for on-demand mouse position calculation + { + ImVec2 plotPos = ImPlot::GetPlotPos(); + ImVec2 plotSize = ImPlot::GetPlotSize(); + config._plotRectMin = plotPos; + config._plotRectMax = ImVec2(plotPos.x + plotSize.x, plotPos.y + plotSize.y); + ImPlotRect limits = ImPlot::GetPlotLimits(); + config._xAxisMin = limits.X.Min; + config._xAxisMax = limits.X.Max; + config._yAxisMin = limits.Y.Min; + config._yAxisMax = limits.Y.Max; + } + if (ImPlot::IsPlotHovered()) { GContext->input.mousePlotPos.x = ImPlot::GetPlotMousePos().x; @@ -1987,7 +2000,7 @@ DearPyGui::draw_image_series(ImDrawList* drawlist, mvAppItem& item, mvImageSerie else texture = static_cast(config._texture.get())->_texture; - ImPlot::PlotImage(item.info.internalLabel.c_str(), texture, config.bounds_min, config.bounds_max, config.uv_min, config.uv_max, config.tintColor, config.flags); + ImPlot::PlotImage(item.info.internalLabel.c_str(), (ImTextureID)(size_t)texture, config.bounds_min, config.bounds_max, config.uv_min, config.uv_max, config.tintColor, config.flags); // Begin a popup for a legend entry. if (ImPlot::BeginLegendPopup(item.info.internalLabel.c_str(), 1)) diff --git a/src/mvPlotting.h b/src/mvPlotting.h index 9d35d90cb..0b15cfd60 100644 --- a/src/mvPlotting.h +++ b/src/mvPlotting.h @@ -437,6 +437,14 @@ struct mvPlotConfig bool localTime = false; bool iSO8601 = false; bool clock24Hour = false; + + // Cached plot geometry for on-demand mouse position calculation + ImVec2 _plotRectMin = {0, 0}; // data area top-left (screen coords) + ImVec2 _plotRectMax = {0, 0}; // data area bottom-right (screen coords) + double _xAxisMin = 0; + double _xAxisMax = 1; + double _yAxisMin = 0; + double _yAxisMax = 1; }; //----------------------------------------------------------------------------- diff --git a/src/mvStyleWindow.cpp b/src/mvStyleWindow.cpp index 58167b4d3..0425e53c8 100644 --- a/src/mvStyleWindow.cpp +++ b/src/mvStyleWindow.cpp @@ -192,7 +192,7 @@ void mvStyleWindow::drawWidgets() "Left-click on color square to open color picker,\n" "Right-click to open edit options menu."); - ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); + ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); ImGui::PushItemWidth(-300); for (int i = 0; i < ImGuiCol_COUNT; i++) { diff --git a/src/mvTextureItems.cpp b/src/mvTextureItems.cpp index 3b7c33b73..782db0e95 100644 --- a/src/mvTextureItems.cpp +++ b/src/mvTextureItems.cpp @@ -30,7 +30,7 @@ void mvTextureRegistry::show_debugger() ImGui::Text("Textures"); - ImGui::BeginChild("##TextureStorageChild", ImVec2(400, 0), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGui::BeginChild("##TextureStorageChild", ImVec2(400, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar); int index = 0; for (auto& texture : childslots[1]) @@ -42,7 +42,7 @@ void mvTextureRegistry::show_debugger() else textureRaw = static_cast(texture.get())->_texture; - ImGui::Image(textureRaw, ImVec2(25, 25)); + ImGui::Image((ImTextureID)(size_t)textureRaw, ImVec2(25, 25)); ImGui::SameLine(); if (ImGui::Selectable(texture->info.internalLabel.c_str(), &status)) _selection = index; @@ -74,13 +74,13 @@ void mvTextureRegistry::show_debugger() else textureRaw = static_cast(childslots[1][_selection].get())->_texture; - ImGui::Image(textureRaw, ImVec2((float)childslots[1][_selection]->config.width, (float)childslots[1][_selection]->config.height)); + ImGui::Image((ImTextureID)(size_t)textureRaw, ImVec2((float)childslots[1][_selection]->config.width, (float)childslots[1][_selection]->config.height)); ImPlot::PushStyleColor(ImPlotCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); if (ImPlot::BeginPlot("##texture plot", ImVec2(-1, -1), ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_Equal)) { - ImPlot::PlotImage(childslots[1][_selection]->info.internalLabel.c_str(), textureRaw, ImPlotPoint(0.0, 0.0), + ImPlot::PlotImage(childslots[1][_selection]->info.internalLabel.c_str(), (ImTextureID)(size_t)textureRaw, ImPlotPoint(0.0, 0.0), ImPlotPoint(childslots[1][_selection]->config.width, childslots[1][_selection]->config.height)); ImPlot::EndPlot(); } @@ -297,9 +297,9 @@ void mvStaticTexture::draw(ImDrawList* drawlist, float x, float y) if (uuid == MV_ATLAS_UUID) { - _texture = ImGui::GetIO().Fonts->TexID; - config.width = ImGui::GetIO().Fonts->TexWidth; - config.height = ImGui::GetIO().Fonts->TexHeight; + _texture = (void*)(size_t)ImGui::GetIO().Fonts->TexRef.GetTexID(); + config.width = ImGui::GetIO().Fonts->TexData->Width; + config.height = ImGui::GetIO().Fonts->TexData->Height; } else _texture = LoadTextureFromArray(_permWidth, _permHeight, _value->data()); diff --git a/src/mvViewport_apple.mm b/src/mvViewport_apple.mm index c2f06e745..688456cd3 100644 --- a/src/mvViewport_apple.mm +++ b/src/mvViewport_apple.mm @@ -218,9 +218,8 @@ if (mvToolManager::GetFontManager().isInvalid()) { mvToolManager::GetFontManager().rebuildAtlas(); - ImGui_ImplMetal_DestroyFontsTexture(); mvToolManager::GetFontManager().updateAtlas(); - ImGui_ImplMetal_CreateFontsTexture(graphicsData->device); + // Font texture is now handled automatically by the backend } } diff --git a/test_drag_range.py b/test_drag_range.py new file mode 100644 index 000000000..7eb8aab17 --- /dev/null +++ b/test_drag_range.py @@ -0,0 +1,86 @@ +import dearpygui.dearpygui as dpg + +dpg.create_context() + +results = [] + +def callback(sender, app_data, user_data): + results.append(f"{user_data}: {app_data}") + print(f"Callback - {user_data}: {app_data}") + +with dpg.window(label="DragRange2 Test", width=500, height=400): + dpg.add_text("Test: DragIntRange2 and DragFloatRange2") + + # Float range + float_range = dpg.add_drag_float_range( + label="Float Range", + default_value=[0.25, 0.75], + min_value=0.0, + max_value=1.0, + speed=0.01, + callback=callback, + user_data="float_range" + ) + + # Int range + int_range = dpg.add_drag_int_range( + label="Int Range", + default_value=[10, 90], + min_value=0, + max_value=100, + callback=callback, + user_data="int_range" + ) + + dpg.add_separator() + + # Test get_value + def test_get_value(): + fv = dpg.get_value(float_range) + iv = dpg.get_value(int_range) + print(f"get_value float_range: {fv}") + print(f"get_value int_range: {iv}") + assert isinstance(fv, list) and len(fv) == 2, f"Float range should return [min, max], got {fv}" + assert isinstance(iv, list) and len(iv) == 2, f"Int range should return [min, max], got {iv}" + print("get_value() test PASSED") + + dpg.add_button(label="Test get_value()", callback=lambda: test_get_value()) + + # Test set_value + def test_set_value(): + dpg.set_value(float_range, [0.1, 0.9]) + dpg.set_value(int_range, [20, 80]) + fv = dpg.get_value(float_range) + iv = dpg.get_value(int_range) + print(f"After set_value - float: {fv}, int: {iv}") + assert abs(fv[0] - 0.1) < 0.01 and abs(fv[1] - 0.9) < 0.01, f"Float set_value failed: {fv}" + assert iv == [20, 80], f"Int set_value failed: {iv}" + print("set_value() test PASSED") + + dpg.add_button(label="Test set_value()", callback=lambda: test_set_value()) + + # With clamped and no_input flags + dpg.add_separator() + dpg.add_text("With flags:") + + dpg.add_drag_float_range( + label="Clamped", + default_value=[0.3, 0.7], + min_value=0.0, + max_value=1.0, + clamped=True + ) + + dpg.add_drag_int_range( + label="No Input", + default_value=[25, 75], + min_value=0, + max_value=100, + no_input=True + ) + +dpg.create_viewport(title='DragRange2 Test', width=600, height=500) +dpg.setup_dearpygui() +dpg.show_viewport() +dpg.start_dearpygui() +dpg.destroy_context() diff --git a/test_drag_range_auto.py b/test_drag_range_auto.py new file mode 100644 index 000000000..77f81d74b --- /dev/null +++ b/test_drag_range_auto.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""Automated test for DragIntRange2 and DragFloatRange2 widgets""" +import dearpygui.dearpygui as dpg + +dpg.create_context() + +# Test 1: Widgets render correctly +print("Test 1: Creating widgets...") +with dpg.window(label="Test", tag="test_window"): + float_range = dpg.add_drag_float_range( + label="Float Range", + default_value=[0.25, 0.75], + min_value=0.0, + max_value=1.0 + ) + int_range = dpg.add_drag_int_range( + label="Int Range", + default_value=[10, 90], + min_value=0, + max_value=100 + ) +print("✓ Widgets created successfully") + +# Test 2: get_value() returns [min, max] list +print("\nTest 2: Testing get_value()...") +fv = dpg.get_value(float_range) +iv = dpg.get_value(int_range) +print(f" float_range: {fv}") +print(f" int_range: {iv}") +assert isinstance(fv, list) and len(fv) == 2, f"Expected [min, max], got {fv}" +assert isinstance(iv, list) and len(iv) == 2, f"Expected [min, max], got {iv}" +assert abs(fv[0] - 0.25) < 0.01 and abs(fv[1] - 0.75) < 0.01, f"Float default wrong: {fv}" +assert iv == [10, 90], f"Int default wrong: {iv}" +print("✓ get_value() returns correct [min, max] list") + +# Test 3: set_value() works +print("\nTest 3: Testing set_value()...") +dpg.set_value(float_range, [0.1, 0.9]) +dpg.set_value(int_range, [20, 80]) +fv = dpg.get_value(float_range) +iv = dpg.get_value(int_range) +print(f" float_range after set: {fv}") +print(f" int_range after set: {iv}") +assert abs(fv[0] - 0.1) < 0.01 and abs(fv[1] - 0.9) < 0.01, f"Float set failed: {fv}" +assert iv == [20, 80], f"Int set failed: {iv}" +print("✓ set_value() works correctly") + +# Test 4: Callbacks fire with correct values +print("\nTest 4: Testing callbacks...") +callback_results = [] +def cb(sender, app_data, user_data): + callback_results.append((user_data, app_data)) + +float_range2 = dpg.add_drag_float_range( + parent="test_window", + label="CB Test", + default_value=[0.5, 0.5], + callback=cb, + user_data="test" +) +# Simulate value change +dpg.set_value(float_range2, [0.3, 0.7]) +# Note: callbacks typically fire on user interaction, not set_value +print("✓ Callback attached successfully") + +# Test 5: Flags work +print("\nTest 5: Testing flags...") +clamped = dpg.add_drag_float_range( + parent="test_window", + label="Clamped", + default_value=[0.3, 0.7], + min_value=0.0, + max_value=1.0, + clamped=True +) +no_input = dpg.add_drag_int_range( + parent="test_window", + label="No Input", + default_value=[25, 75], + no_input=True +) +print("✓ Flags (clamped, no_input) accepted") + +print("\n" + "="*50) +print("ALL TESTS PASSED") +print("="*50) + +dpg.destroy_context() diff --git a/test_step.py b/test_step.py new file mode 100644 index 000000000..c14d59a8c --- /dev/null +++ b/test_step.py @@ -0,0 +1,82 @@ +import dearpygui.dearpygui as dpg + +dpg.create_context() + +def print_value(sender, app_data, user_data): + print(f"{user_data}: {app_data}") + +with dpg.window(label="Step/Snapping Test", width=500, height=400): + dpg.add_text("Float Range Sliders with different step values:") + + # No snapping + dpg.add_slider_float_range( + label="No step (smooth)", + default_value=[0.2, 0.8], + min_value=0.0, + max_value=1.0, + callback=print_value, + user_data="No step" + ) + + # Snap to 0.1 + dpg.add_slider_float_range( + label="step=0.1", + default_value=[0.2, 0.8], + min_value=0.0, + max_value=1.0, + step=0.1, + callback=print_value, + user_data="step=0.1" + ) + + # Snap to 0.25 + dpg.add_slider_float_range( + label="step=0.25", + default_value=[0.25, 0.75], + min_value=0.0, + max_value=1.0, + step=0.25, + callback=print_value, + user_data="step=0.25" + ) + + dpg.add_separator() + dpg.add_text("Int Range Sliders with different step values:") + + # No snapping + dpg.add_slider_int_range( + label="No step (smooth)", + default_value=[10, 90], + min_value=0, + max_value=100, + callback=print_value, + user_data="Int: No step" + ) + + # Snap to 5 + dpg.add_slider_int_range( + label="step=5", + default_value=[10, 90], + min_value=0, + max_value=100, + step=5, + callback=print_value, + user_data="Int: step=5" + ) + + # Snap to 10 + dpg.add_slider_int_range( + label="step=10", + default_value=[20, 80], + min_value=0, + max_value=100, + step=10, + callback=print_value, + user_data="Int: step=10" + ) + +dpg.create_viewport(title='Step/Snapping Test', width=600, height=500) +dpg.setup_dearpygui() +dpg.show_viewport() +dpg.start_dearpygui() +dpg.destroy_context() diff --git a/thirdparty/imgui b/thirdparty/imgui index 139e99ca3..4e6083c88 160000 --- a/thirdparty/imgui +++ b/thirdparty/imgui @@ -1 +1 @@ -Subproject commit 139e99ca37a3e127c87690202faec005cd892d36 +Subproject commit 4e6083c88c73ecdfe7dfd5e877f8b8fdb2ac7f91 diff --git a/thirdparty/imnodes/imnodes.cpp b/thirdparty/imnodes/imnodes.cpp index bea71c99e..b82cfaf37 100644 --- a/thirdparty/imnodes/imnodes.cpp +++ b/thirdparty/imnodes/imnodes.cpp @@ -354,7 +354,7 @@ void ImDrawListGrowChannels(ImDrawList* draw_list, const int num_channels) { ImDrawCmd draw_cmd; draw_cmd.ClipRect = draw_list->_ClipRectStack.back(); - draw_cmd.TextureId = draw_list->_TextureIdStack.back(); + draw_cmd.TexRef = draw_list->_TextureStack.back(); channel._CmdBuffer.push_back(draw_cmd); } } diff --git a/thirdparty/implot b/thirdparty/implot index f156599fa..c5bf211a8 160000 --- a/thirdparty/implot +++ b/thirdparty/implot @@ -1 +1 @@ -Subproject commit f156599faefe316f7dd20fe6c783bf87c8bb6fd9 +Subproject commit c5bf211a8cf6191cc0a4f1ad77ffa8dd29088361