From 0cf7ace207b803fd4e5e5829ba9617bb591f3701 Mon Sep 17 00:00:00 2001
From: Ivanzzzc <46572469+IvanNazaruk@users.noreply.github.com>
Date: Sun, 1 Jan 2023 23:13:33 +0200
Subject: [PATCH] v1.0.0
---
.gitignore | 2 +
DearPyGui_DragAndDrop/__init__.py | 16 ++++
DearPyGui_DragAndDrop/main.py | 145 ++++++++++++++++++++++++++++++
DearPyGui_DragAndDrop/setup.py | 64 +++++++++++++
DearPyGui_DragAndDrop/tools.py | 87 ++++++++++++++++++
Examples/EADME.md | 50 +++++++++++
Examples/example1.py | 25 ++++++
Examples/example2.py | 39 ++++++++
Examples/example3.py | 58 ++++++++++++
Examples/example4.py | 80 +++++++++++++++++
Examples/example5.py | 59 ++++++++++++
README.md | 60 +++++++++++++
pyproject.toml | 31 +++++++
13 files changed, 716 insertions(+)
create mode 100644 DearPyGui_DragAndDrop/__init__.py
create mode 100644 DearPyGui_DragAndDrop/main.py
create mode 100644 DearPyGui_DragAndDrop/setup.py
create mode 100644 DearPyGui_DragAndDrop/tools.py
create mode 100644 Examples/EADME.md
create mode 100644 Examples/example1.py
create mode 100644 Examples/example2.py
create mode 100644 Examples/example3.py
create mode 100644 Examples/example4.py
create mode 100644 Examples/example5.py
create mode 100644 README.md
create mode 100644 pyproject.toml
diff --git a/.gitignore b/.gitignore
index b6e4761..6c2e3e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+.idea/
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
diff --git a/DearPyGui_DragAndDrop/__init__.py b/DearPyGui_DragAndDrop/__init__.py
new file mode 100644
index 0000000..384f583
--- /dev/null
+++ b/DearPyGui_DragAndDrop/__init__.py
@@ -0,0 +1,16 @@
+import threading
+
+from . import main as __main
+from . import setup as __setup
+from .main import DROPEFFECT, KEYSTATE
+from .main import DragAndDrop, DragAndDropDataObject
+from .main import set_drag_enter, set_drag_over, set_drag_leave, set_drop
+from .main import set_drop_effect, get_drop_effect
+
+__version__ = "1.0.0"
+
+def initialize():
+ if isinstance(__main._DragAndDropForFunctions, type):
+ __main._DragAndDropForFunctions = __main._DragAndDropForFunctions()
+
+ threading.Thread(target=__setup.setup, daemon=True).start()
diff --git a/DearPyGui_DragAndDrop/main.py b/DearPyGui_DragAndDrop/main.py
new file mode 100644
index 0000000..fc7463a
--- /dev/null
+++ b/DearPyGui_DragAndDrop/main.py
@@ -0,0 +1,145 @@
+from __future__ import annotations
+
+import traceback
+from enum import IntEnum
+from pathlib import Path
+from typing import Type, TypeVar, Dict, Callable, Any, Union, List
+
+import dearpygui.dearpygui as dpg
+
+DragAndDropDataObject = Union[None, str, List[Path]]
+SubscriptionTag = TypeVar('SubscriptionTag', bound=int)
+
+
+class KEYSTATE(IntEnum):
+ LEFT = 1
+ RIGHT = 2
+ SHIFT = 4
+ CTRL = 8
+ MIDDLE = 16
+ ALT = 32
+
+
+class DROPEFFECT(IntEnum):
+ NONE = 0
+ COPY = 1
+ MOVE = 2
+
+
+_now_drop_effect: DROPEFFECT = DROPEFFECT.MOVE
+
+
+def set_drop_effect(effect: DROPEFFECT = DROPEFFECT.NONE):
+ global _now_drop_effect
+ _now_drop_effect = effect
+
+
+def get_drop_effect() -> DROPEFFECT:
+ return _now_drop_effect
+
+
+class DragAndDrop():
+ __subscribers: Dict[SubscriptionTag, Type[DragAndDrop]] = {}
+ __subscription_tag: SubscriptionTag = None
+
+ def DragEnter(self, dataObject: DragAndDropDataObject, keyState: list[KEYSTATE]):
+ ...
+
+ def DragOver(self, keyState: list[KEYSTATE]):
+ ...
+
+ def DragLeave(self):
+ ...
+
+ def Drop(self, dataObject: DragAndDropDataObject, keyState: list[KEYSTATE]):
+ ...
+
+ def __init__(self):
+ if self.__subscription_tag:
+ self._unsubscribe(self.__subscription_tag)
+ self.__subscription_tag = self._subscribe(self) # noqa
+
+ def __del__(self):
+ if self.__subscription_tag:
+ self._unsubscribe(self.__subscription_tag)
+
+ @classmethod
+ def _subscribe(cls, self: Type[DragAndDrop]) -> SubscriptionTag:
+ subscription_tag = dpg.generate_uuid()
+ cls.__subscribers[subscription_tag] = self
+ return subscription_tag
+
+ @classmethod
+ def _unsubscribe(self, subscription_tag: SubscriptionTag):
+ if subscription_tag in self.__subscribers:
+ del self.__subscribers[subscription_tag]
+
+ @classmethod
+ def _DragEnter(cls, dataObject, keyState):
+ for self in cls.__subscribers.values():
+ try:
+ self.DragEnter(dataObject, keyState) # noqa
+ except Exception:
+ traceback.print_exc()
+
+ @classmethod
+ def _DragOver(cls, keyState):
+ for self in cls.__subscribers.values():
+ try:
+ self.DragOver(keyState) # noqa
+ except Exception:
+ traceback.print_exc()
+
+ @classmethod
+ def _DragLeave(cls):
+ for self in cls.__subscribers.values():
+ try:
+ self.DragLeave() # noqa
+ except Exception:
+ traceback.print_exc()
+
+ @classmethod
+ def _Drop(cls, dataObject, keyState):
+ for self in cls.__subscribers.values():
+ try:
+ self.Drop(dataObject, keyState) # noqa
+ except Exception:
+ traceback.print_exc()
+
+
+class _DragAndDropForFunctions(DragAndDrop):
+ def DragEnter(self, dataObject, keyState):
+ ...
+
+ def DragOver(self, keyState):
+ ...
+
+ def DragLeave(self):
+ ...
+
+ def Drop(self, dataObject, keyState):
+ ...
+
+
+def set_drag_enter(function: Callable[[DragAndDropDataObject, list[KEYSTATE]], Any] = None):
+ if function is None:
+ function = lambda *args, **kwargs: ...
+ _DragAndDropForFunctions.DragEnter = function
+
+
+def set_drag_over(function: Callable[[list[KEYSTATE]], Any] = None):
+ if function is None:
+ function = lambda *args, **kwargs: ...
+ _DragAndDropForFunctions.DragOver = function
+
+
+def set_drag_leave(function: Callable[[], Any] = None):
+ if function is None:
+ function = lambda *args, **kwargs: ...
+ _DragAndDropForFunctions.DragLeave = function
+
+
+def set_drop(function: Callable[[DragAndDropDataObject, list[KEYSTATE]], Any] = None):
+ if function is None:
+ function = lambda *args, **kwargs: ...
+ _DragAndDropForFunctions.Drop = function
diff --git a/DearPyGui_DragAndDrop/setup.py b/DearPyGui_DragAndDrop/setup.py
new file mode 100644
index 0000000..da938bc
--- /dev/null
+++ b/DearPyGui_DragAndDrop/setup.py
@@ -0,0 +1,64 @@
+import ctypes
+import os
+import time
+
+import dearpygui.dearpygui as dpg
+import pythoncom
+import win32gui
+from win32com.server.policy import DesignatedWrapPolicy
+
+from . import tools
+from .main import DragAndDrop
+from .main import get_drop_effect
+
+hwnd = -1
+
+
+class DropTarget(DesignatedWrapPolicy):
+ _public_methods_ = ['DragEnter', 'DragOver', 'DragLeave', 'Drop']
+ _com_interfaces_ = [pythoncom.IID_IDropTarget] # noqa
+
+ def __init__(self): # noqa
+ self._wrap_(self)
+
+ def DragEnter(self, dataObject, keyState, point, effect):
+ ctypes.windll.user32.SetForegroundWindow(hwnd)
+ DragAndDrop._DragEnter(tools.get_data_from_dataObject(dataObject),
+ tools.key_state_to_keys_list(keyState))
+ return 0, get_drop_effect()
+ # return winerror.S_OK, get_drop_effect()
+
+ def DragOver(self, keyState, point, effect):
+ DragAndDrop._DragOver(tools.key_state_to_keys_list(keyState))
+ return get_drop_effect()
+
+ def DragLeave(self):
+ DragAndDrop._DragLeave()
+
+ def Drop(self, dataObject, keyState, point, effect):
+ DragAndDrop._Drop(tools.get_data_from_dataObject(dataObject),
+ tools.key_state_to_keys_list(keyState))
+
+
+def setup():
+ global hwnd
+ if hwnd != -1:
+ return
+ hwnd = 0
+ # Wait for initialization and appearance of the DPG window
+ while dpg.get_frame_count() == 0:
+ time.sleep(0.1)
+
+ # Get the window hwnd from its own pid
+ hwnd = tools.get_hwnd_from_pid(os.getpid())
+
+ pythoncom.OleInitialize() # noqa
+ pythoncom.RegisterDragDrop( # noqa
+ hwnd,
+ pythoncom.WrapObject( # noqa
+ DropTarget(),
+ pythoncom.IID_IDropTarget, # noqa
+ pythoncom.IID_IDropTarget # noqa
+ )
+ )
+ win32gui.PumpMessages()
diff --git a/DearPyGui_DragAndDrop/tools.py b/DearPyGui_DragAndDrop/tools.py
new file mode 100644
index 0000000..1430373
--- /dev/null
+++ b/DearPyGui_DragAndDrop/tools.py
@@ -0,0 +1,87 @@
+from __future__ import annotations
+
+import ctypes.wintypes
+from ctypes import wintypes
+
+import pythoncom
+import pywintypes
+import win32con
+from win32comext.shell import shell
+
+from .main import DragAndDropDataObject
+from .main import KEYSTATE
+
+
+def get_data_from_dataObject(dataObject) -> DragAndDropDataObject:
+ try:
+ # Drag&Drop files
+ _format = win32con.CF_HDROP, None, 1, -1, pythoncom.TYMED_HGLOBAL # noqa
+ sm = dataObject.GetData(_format)
+ except pywintypes.com_error: # noqa
+ try:
+ # Drag&Drop text
+ _format = win32con.CF_TEXT, None, 1, -1, pythoncom.TYMED_HGLOBAL # noqa
+ sm = dataObject.GetData(_format)
+ # returns a string converted from bytes
+ return sm.data.decode("utf-8")
+ except pywintypes.com_error: # noqa
+ # Drag&Drop unknown type
+ # return nothing
+ return None
+
+ # Drag&Drop files
+ num_files = shell.DragQueryFile(sm.data_handle, -1)
+ files = []
+ for i in range(num_files):
+ fpath = shell.DragQueryFile(sm.data_handle, i)
+ files.append(fpath)
+ # returns a list of file paths
+ return files
+
+
+user32 = ctypes.windll.user32
+WNDENUMPROC = ctypes.WINFUNCTYPE(wintypes.BOOL,
+ wintypes.HWND,
+ wintypes.LPARAM)
+user32.EnumWindows.argtypes = [WNDENUMPROC,
+ wintypes.LPARAM]
+
+
+def get_hwnd_from_pid(pid: int) -> int | None:
+ result = None
+
+ def callback(hwnd, _):
+ nonlocal result
+ lpdw_PID = ctypes.c_ulong()
+ user32.GetWindowThreadProcessId(hwnd, ctypes.byref(lpdw_PID))
+ hwnd_PID = lpdw_PID.value
+
+ if hwnd_PID == pid:
+ result = hwnd
+ return False
+ return True
+
+ cb_worker = WNDENUMPROC(callback)
+ user32.EnumWindows(cb_worker, 0)
+ return result
+
+
+def key_state_to_keys_list(keyState: int) -> list[KEYSTATE]:
+ # https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.drageventargs.keystate?view=windowsdesktop-7.0#remarks
+ key_state_list = []
+
+ # conversion to byte string and reverse it
+ keyState = '{0:06b}'.format(keyState)[::-1]
+ if bool(int(keyState[0])):
+ key_state_list.append(KEYSTATE.LEFT)
+ if bool(int(keyState[1])):
+ key_state_list.append(KEYSTATE.RIGHT)
+ if bool(int(keyState[2])):
+ key_state_list.append(KEYSTATE.SHIFT)
+ if bool(int(keyState[3])):
+ key_state_list.append(KEYSTATE.CTRL)
+ if bool(int(keyState[4])):
+ key_state_list.append(KEYSTATE.MIDDLE)
+ if bool(int(keyState[5])):
+ key_state_list.append(KEYSTATE.ALT)
+ return key_state_list
diff --git a/Examples/EADME.md b/Examples/EADME.md
new file mode 100644
index 0000000..857bbae
--- /dev/null
+++ b/Examples/EADME.md
@@ -0,0 +1,50 @@
+# Examples
+
+You can find several scenarios here, from simple to advanced, for using the library.
+Below you can see what they look like and their brief description.
+
+## [Example №1: the simplest use case](example1.py)
+
+Probably the simplest and most used example of using.
+Drop the file into the program and our `drop` (change text) function will be called.
+![example_1](https://user-images.githubusercontent.com/46572469/210179706-f187b70b-6649-44d4-880b-a6b35a961d08.gif)
+
+## [Example №2: little interactivity](example2.py)
+
+An example with a little interactivity, in the form of a pop-up window that says `Drop!`
+(This is "similar" to the popup in Discord when you try to send a file by drag and drop).
+This one has more functions, but also seems to be nothing complicated.
+In `drag_enter` and `drag_leave` we hide and show the window respectively, and in `drop` we add hiding the window.
+![example_2](https://user-images.githubusercontent.com/46572469/210180323-1ab73614-0f35-49e8-b5e1-7c65c332e44b.gif)
+
+## [Example №3: more interactivity!](example3.py)
+
+In this example, you can only drop a file in the popup window.
+Added a function `drag_over` which checks the pointing to the window and, depending on the result,
+changes the theme of the window and the style of the cursor.
+![example_3](https://user-images.githubusercontent.com/46572469/210180650-b91c77d8-19da-4452-ad69-f8caa0201c32.gif)
+
+## [Example №4: keys usage](example4.py)
+
+Let's expand our example again, now the window theme depends on the key presses (CTRL, ALT, SHIFT).
+![example_4](https://user-images.githubusercontent.com/46572469/210181492-3a9bca4a-005a-4e5c-b8cf-4a39b041ef27.gif)
+
+## [Example №5: inheriting *DragAndDrop* or how to add an extra function to Drag And Drop](example4.py)
+
+Probably you need a lot of *DragAndDrop* functions when developing some applications,
+and only one function can be attached to `set_drop`.
+And there is a solution for this, namely to use the class *DragAndDrop*, or rather inherit it.
+When `__init__` is called in *DragAndDrop* (`super().__init__()`),
+this object (`self`) gets to the Drag-and-Drop functions queue,
+where certain functions will be called in the future, for example `{DragAndDrop_oject}.DragOver`.
+This example uses only `DragOver` and `Drop`, but also has `DragEnter` and `DragLeave`
+which correspond to the functions `.set_drag_enter` and `.set_drag_leave` respectively.
+I would also like to add that all functions `.set_*` are called first in the queue
+(if you `.initialize()` before the DragAndDrop classes).
+And the "function" of setting the cursor style will be called after the queue is finished
+(that is, the last called `.set_drop_effect` will be taken).
+
+A little about the example: there are several "areas" in
+which you can drop a file and the text in them will change when it `drop`
+
+![example_5](https://user-images.githubusercontent.com/46572469/210182508-d2232d43-0df3-4531-a34b-e773f9000ef9.gif)
diff --git a/Examples/example1.py b/Examples/example1.py
new file mode 100644
index 0000000..c14aa0e
--- /dev/null
+++ b/Examples/example1.py
@@ -0,0 +1,25 @@
+import dearpygui.dearpygui as dpg
+
+import DearPyGui_DragAndDrop as dpg_dnd
+
+dpg.create_context()
+dpg_dnd.initialize()
+dpg.create_viewport(title="Drag and drop example1", width=600, height=600)
+
+with dpg.window() as window:
+ drop_text = dpg.add_text()
+ drop_keys = dpg.add_text()
+dpg.set_primary_window(window, True)
+
+
+def drop(data, keys):
+ dpg.set_value(drop_text, f'{data}')
+ dpg.set_value(drop_keys, f'{keys}')
+
+
+dpg_dnd.set_drop(drop)
+
+dpg.setup_dearpygui()
+dpg.show_viewport()
+dpg.start_dearpygui()
+dpg.destroy_context()
diff --git a/Examples/example2.py b/Examples/example2.py
new file mode 100644
index 0000000..7816eaa
--- /dev/null
+++ b/Examples/example2.py
@@ -0,0 +1,39 @@
+import dearpygui.dearpygui as dpg
+
+import DearPyGui_DragAndDrop as dpg_dnd
+
+dpg.create_context()
+dpg_dnd.initialize()
+dpg.create_viewport(title="Drag and drop example2", width=600, height=600)
+
+with dpg.window() as window:
+ drop_text = dpg.add_text()
+ drop_keys = dpg.add_text()
+dpg.set_primary_window(window, True)
+
+with dpg.window(modal=True, autosize=True, show=False) as drop_window:
+ dpg.add_text("Drop!", color=(0, 255, 0))
+
+
+def drop(data, keys):
+ dpg.configure_item(drop_window, show=False)
+ dpg.set_value(drop_text, f'{data}')
+ dpg.set_value(drop_keys, f'{keys}')
+
+
+def drag_enter(data, keys):
+ dpg.configure_item(drop_window, show=True)
+
+
+def drag_leave():
+ dpg.configure_item(drop_window, show=False)
+
+
+dpg_dnd.set_drop(drop)
+dpg_dnd.set_drag_enter(drag_enter)
+dpg_dnd.set_drag_leave(drag_leave)
+
+dpg.setup_dearpygui()
+dpg.show_viewport()
+dpg.start_dearpygui()
+dpg.destroy_context()
diff --git a/Examples/example3.py b/Examples/example3.py
new file mode 100644
index 0000000..a28a422
--- /dev/null
+++ b/Examples/example3.py
@@ -0,0 +1,58 @@
+import dearpygui.dearpygui as dpg
+
+import DearPyGui_DragAndDrop as dpg_dnd
+
+dpg.create_context()
+dpg_dnd.initialize()
+dpg.create_viewport(title="Drag and drop example3", width=600, height=600)
+
+with dpg.theme() as hover_drag_theme:
+ with dpg.theme_component(dpg.mvAll):
+ dpg.add_theme_color(dpg.mvThemeCol_Border, (0, 180, 255), category=dpg.mvThemeCat_Core)
+ dpg.add_theme_color(dpg.mvThemeCol_PopupBg, (25, 50, 75), category=dpg.mvThemeCat_Core)
+
+with dpg.window() as window:
+ drop_text = dpg.add_text()
+ drop_keys = dpg.add_text()
+dpg.set_primary_window(window, True)
+
+with dpg.window(modal=True, autosize=True, show=False) as drop_window:
+ dpg.add_text("Drop here!", color=(0, 255, 0))
+
+
+def drop(data, keys):
+ dpg.configure_item(drop_window, show=False)
+ dpg.bind_item_theme(drop_window, None)
+ dpg_dnd.set_drop_effect()
+
+ dpg.set_value(drop_text, f'{data}')
+ dpg.set_value(drop_keys, f'{keys}')
+
+
+def drag_over(keys):
+ if dpg.is_item_hovered(drop_window):
+ dpg.bind_item_theme(drop_window, hover_drag_theme)
+ dpg_dnd.set_drop_effect(dpg_dnd.DROPEFFECT.MOVE)
+ else:
+ dpg.bind_item_theme(drop_window, None)
+ dpg_dnd.set_drop_effect()
+
+
+def drag_enter(data, keys):
+ dpg.configure_item(drop_window, show=True)
+
+
+def drag_leave():
+ dpg.configure_item(drop_window, show=False)
+ dpg.bind_item_theme(drop_window, None)
+
+
+dpg_dnd.set_drop(drop)
+dpg_dnd.set_drag_over(drag_over)
+dpg_dnd.set_drag_enter(drag_enter)
+dpg_dnd.set_drag_leave(drag_leave)
+
+dpg.setup_dearpygui()
+dpg.show_viewport()
+dpg.start_dearpygui()
+dpg.destroy_context()
diff --git a/Examples/example4.py b/Examples/example4.py
new file mode 100644
index 0000000..fc619d0
--- /dev/null
+++ b/Examples/example4.py
@@ -0,0 +1,80 @@
+import dearpygui.dearpygui as dpg
+
+import DearPyGui_DragAndDrop as dpg_dnd
+
+dpg.create_context()
+dpg_dnd.initialize()
+dpg.create_viewport(title="Drag and drop example4", width=600, height=600)
+
+with dpg.theme() as hover_drag_theme_1:
+ with dpg.theme_component(dpg.mvAll):
+ dpg.add_theme_color(dpg.mvThemeCol_Border, (0, 180, 255), category=dpg.mvThemeCat_Core)
+ dpg.add_theme_color(dpg.mvThemeCol_PopupBg, (25, 50, 75), category=dpg.mvThemeCat_Core)
+with dpg.theme() as hover_drag_theme_2:
+ with dpg.theme_component(dpg.mvAll):
+ dpg.add_theme_color(dpg.mvThemeCol_Border, (250, 5, 60), category=dpg.mvThemeCat_Core)
+ dpg.add_theme_color(dpg.mvThemeCol_PopupBg, (45, 15, 5), category=dpg.mvThemeCat_Core)
+with dpg.theme() as hover_drag_theme_3:
+ with dpg.theme_component(dpg.mvAll):
+ dpg.add_theme_color(dpg.mvThemeCol_Border, (205, 0, 255), category=dpg.mvThemeCat_Core)
+ dpg.add_theme_color(dpg.mvThemeCol_PopupBg, (65, 30, 100), category=dpg.mvThemeCat_Core)
+with dpg.theme() as hover_drag_theme_4:
+ with dpg.theme_component(dpg.mvAll):
+ dpg.add_theme_color(dpg.mvThemeCol_Border, (0, 255, 60), category=dpg.mvThemeCat_Core)
+ dpg.add_theme_color(dpg.mvThemeCol_PopupBg, (30, 65, 30), category=dpg.mvThemeCat_Core)
+
+with dpg.window() as window:
+ drop_text = dpg.add_text()
+ drop_keys = dpg.add_text()
+dpg.set_primary_window(window, True)
+
+with dpg.window(modal=True, autosize=True, show=False) as drop_window:
+ dpg.add_text("Drop here!", color=(0, 255, 0))
+
+
+def drop(data, keys):
+ dpg.configure_item(drop_window, show=False)
+ dpg.bind_item_theme(drop_window, None)
+ dpg_dnd.set_drop_effect()
+
+ dpg.set_value(drop_text, f'{data}')
+ dpg.set_value(drop_keys, f'{keys}')
+
+
+def drag_over(keys):
+ if not dpg.is_item_hovered(drop_window):
+ dpg.bind_item_theme(drop_window, None)
+ dpg_dnd.set_drop_effect()
+ return
+
+ hover_drag_theme = hover_drag_theme_1
+ if dpg_dnd.KEYSTATE.ALT in keys and dpg_dnd.KEYSTATE.CTRL in keys:
+ hover_drag_theme = hover_drag_theme_2
+ elif dpg_dnd.KEYSTATE.ALT in keys:
+ hover_drag_theme = hover_drag_theme_3
+ elif dpg_dnd.KEYSTATE.CTRL in keys:
+ hover_drag_theme = hover_drag_theme_4
+
+ dpg.bind_item_theme(drop_window, hover_drag_theme)
+ dpg_dnd.set_drop_effect(dpg_dnd.DROPEFFECT.MOVE)
+
+
+def drag_enter(data, keys):
+ dpg.configure_item(drop_window, show=True)
+
+
+def drag_leave():
+ dpg.configure_item(drop_window, show=False)
+ dpg.bind_item_theme(drop_window, None)
+ dpg_dnd.set_drop_effect()
+
+
+dpg_dnd.set_drop(drop)
+dpg_dnd.set_drag_over(drag_over)
+dpg_dnd.set_drag_enter(drag_enter)
+dpg_dnd.set_drag_leave(drag_leave)
+
+dpg.setup_dearpygui()
+dpg.show_viewport()
+dpg.start_dearpygui()
+dpg.destroy_context()
diff --git a/Examples/example5.py b/Examples/example5.py
new file mode 100644
index 0000000..18c1b64
--- /dev/null
+++ b/Examples/example5.py
@@ -0,0 +1,59 @@
+import dearpygui.dearpygui as dpg
+
+import DearPyGui_DragAndDrop as dpg_dnd
+
+dpg.create_context()
+dpg_dnd.initialize()
+dpg.create_viewport(title="Drag and drop example5", width=600, height=600)
+
+with dpg.theme() as hover_drag_theme:
+ with dpg.theme_component(dpg.mvAll):
+ dpg.add_theme_color(dpg.mvThemeCol_Border, (0, 180, 255), category=dpg.mvThemeCat_Core)
+ dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (25, 50, 75), category=dpg.mvThemeCat_Core)
+
+
+class ChildWindow(dpg_dnd.DragAndDrop):
+ window: int = None
+ dpg_text: int
+
+ def __init__(self):
+ super().__init__()
+
+ def create(self):
+ with dpg.child_window(width=-1, height=50) as self.window:
+ self.dpg_text = dpg.add_text(parent=self.window)
+
+ def DragOver(self, keyState: list[dpg_dnd.KEYSTATE]):
+ if self.window is None:
+ return
+ if dpg.is_item_hovered(self.window):
+ dpg.bind_item_theme(self.window, hover_drag_theme)
+ dpg_dnd.set_drop_effect(dpg_dnd.DROPEFFECT.MOVE)
+ else:
+ dpg.bind_item_theme(self.window, None)
+
+ def Drop(self, dataObject: dpg_dnd.DragAndDropDataObject, keyState: list[dpg_dnd.KEYSTATE]):
+ if self.window is None:
+ return
+ dpg.bind_item_theme(self.window, None)
+ if dpg.is_item_hovered(self.window):
+ dpg.set_value(self.dpg_text, f'{dataObject}')
+
+def drag_over(keys):
+ dpg_dnd.set_drop_effect()
+
+
+dpg_dnd.set_drag_over(drag_over)
+
+with dpg.window() as window:
+ for i in range(5):
+ child_window = ChildWindow()
+ child_window.create()
+ dpg.add_spacer(height=25)
+
+dpg.set_primary_window(window, True)
+
+dpg.setup_dearpygui()
+dpg.show_viewport()
+dpg.start_dearpygui()
+dpg.destroy_context()
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a9e403d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,60 @@
+# DearPyGui-DragAndDrop
+
+Working Drag-and-drop for DearPyGui, Windows only
+![demo](https://user-images.githubusercontent.com/46572469/210178591-3011e753-82ed-4d8f-8652-87375d7366e0.gif)
+
+## How to install/use
+
+1) Install the DearPyGui-DragAndDrop package:
+ `pip install DearPyGui-DragAndDrop`
+
+2) Import and then initialize the library after `dpg.create_context()`:
+
+```python
+import dearpygui.dearpygui as dpg
+
+import DearPyGui_DragAndDrop as dpg_dnd
+
+dpg.create_context()
+dpg_dnd.initialize()
+```
+
+3) That's it, just set your function for `drop` and your minimal use case is ready:
+
+```python
+import dearpygui.dearpygui as dpg
+
+import DearPyGui_DragAndDrop as dpg_dnd
+
+dpg.create_context()
+dpg_dnd.initialize()
+dpg.create_viewport(title="Drag and drop example", width=600, height=600)
+
+
+def drop(data, keys):
+ print(f'{data}')
+ print(f'{keys}')
+
+
+dpg_dnd.set_drop(drop)
+
+dpg.setup_dearpygui()
+dpg.show_viewport()
+dpg.start_dearpygui()
+dpg.destroy_context()
+```
+
+### More examples of use are in the [Examples folder](https://github.com/IvanNazaruk/DearPyGui-DragAndDrop/Examples)
+
+## Supported Formats
+
+- [x] File(s) paths *(will be in the form of a `list of strings`)*
+- [x] Text *(will be `string`)*
+
+Everything else will be `None` or will be a `string`
+
+## TODO list:
+
+- [ ] Documentation and code/functions comments
+- [ ] Add support for more and other data
+ types: [maybe this is the right list](https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats#constants)
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..644b6d9
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,31 @@
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "DearPyGui-DragAndDrop"
+version = "1.0.0"
+authors = [
+ { name = "Ivan Nazaruk" },
+]
+description = "Working Drag-and-Drop for DearPyGui, Windows only"
+readme = "README.md"
+requires-python = ">=3.7"
+license = { text = "MIT" }
+classifiers = [
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3",
+ "Operating System :: Microsoft :: Windows"
+]
+dependencies = [
+ "pywin32",
+ "dearpygui>=1.4"
+]
+
+[tool.setuptools]
+packages = ["DearPyGui_DragAndDrop"]
+
+[project.urls]
+"Homepage" = "https://github.com/IvanNazaruk/DearPyGui-DragAndDrop"
+"Bug Tracker" = "https://github.com/IvanNazaruk/DearPyGui-DragAndDrop/issues"
\ No newline at end of file