Drag and drop, fingers and pens (platform)
The API is the same on every system (src/platform/dragdrop.odin, src/platform/pointer.odin). Windows implements it
(dragdrop_win32.odin: OLE; pointer_win32.odin: WM_POINTER), Linux too (dragdrop_linux.odin: wl_data_device, XDND).
Other systems build with dragdrop_stub.odin, which answers "not supported" (drop_target_enable returns false,
drag_start returns ok = false). Pointer_* events come on Windows only.
Receiving drops
platform.drop_target_enable(win, true) // false: refused (no OLE on this thread, an elevated process)
for e in platform.poll_events(win, false) {
#partial switch e.kind {
case .Drop_Enter, .Drop_Over:
d := platform.drop_data(win) // files, text, html, url (+ url_title), image (PNG), custom entries
platform.drop_set_effect(win, accepts(d, e.pos) ? platform.drop_effect_for(e.drop_allowed, e.mods) : nil)
case .Drop:
handle(platform.drop_data(win)^, e.pos)
platform.drop_data_release(win)
case .Drop_Leave:
}
}- Events come at the pointer's position (client pixels), with the modifiers held and
drop_allowed(what the source allows). The data is read once when the drag enters (and again at the drop, for sources that render late). - The system asks synchronously what a drop would do; the app answers in its next frame with
drop_set_effect. The window gives the system the app's last answer (nil = refused). This is what browsers do with their renderer. drop_effect_for(allowed, mods, preferred): the usual convention (Ctrl copies, Shift moves, Ctrl+Shift or Alt links).- Formats on Windows:
CF_HDROP,CF_UNICODETEXT,HTML Format(the fragment is returned without its header:html_format_fragment),UniformResourceLocatorW/UniformResourceLocator,PNG, and a private format for the app's own MIME types (Drag_Data.custom,drag_custom_encode).
Starting a drag
d: platform.Drag_Data
d.url, d.text = "https://example.com/", "Example"
effect, ok := platform.drag_start(win, d, {.Copy, .Link}, platform.Drag_Image{rgba, {w, h}, {w / 2, h / 2}})drag_start blocks while the user drags (the system's loop runs) and returns the effect the target chose, nil when
the drag was given up. Call it between frames, from the press-and-move that started the drag; an app that wants
in-window drags without the system's loop handles those itself and calls drag_start when the pointer leaves the
window. drag_threshold(win) is the distance the system uses before a press becomes a drag.
Call dragdrop_thread_init() first thing on each interface thread (Windows: OLE needs a single-threaded apartment,
which a decoder library joining the multithreaded one first would make impossible). OLE is initialized once per thread:
drop_target_enable and drag_start ask the same, and a thread in the multithreaded apartment does not stop another.
The drag image
Drag_Image{rgba, size, offset}: straight RGBA, the hot spot (offset) is where the pointer is in the picture.
drag_image_fade(img, 0.7) makes the usual translucent ghost, drag_image_fit(img, {480, 480}) a copy that fits.
After drag_start, drag_image_held(win) gives the size and hot spot the system holds (what it draws over every
application).
The app's own drag coming back
drag_start blocks: an app that drags in its own window then hands the drag to the system when the pointer leaves
cannot run a frame when it comes back. drop_sync_set(win, handler, user) makes the window ask handler at once for
each Drop_* (it returns the effect, and handles the Drop inside the call), so the drop is handled before
drag_start returns.
Tests and remote control
drag_script(win, steps): the nextdrag_startfollowssteps(client points; outside the client area = over another application; the last one is the release) through the window's own drop target, never the real mouse.
drop_inject(win, kind, pos, &data): the same events with data of the app's choice, throughinject_event.- Every event made by
inject_eventhasEvent.injectedset: an app that reads the pointer's screen position from the system during a drag of its own (a window carried across the screen) takes the event's position for these instead. drop_self_test(win, data, pos, allowed)(Windows): the data objectdrag_startwould build goes through the window's own drop target, so both conversions and the events are tested without a mouse (tests/dragdrop).
Fingers and pens
Pointer_Down, Pointer_Move, Pointer_Up, Pointer_Cancel carry Event.pointer (Pointer_Info: id, kind
touch or pen, primary, pressure 0..1, pen tilt / twist / barrel / eraser, touch contact size). The mouse
keeps Mouse_*, which now also carry buttons (the buttons held after the event). The system's emulation of the mouse
for a finger or a pen still arrives, marked emulated: an app that only knows the mouse works unchanged, one that
follows pointers skips those (Event.pointer.kind says whether a finger or a pen is emulated). pointer_buttons and
pointer_pressure give the Pointer Events values.
input_devices() (mouse, pen, touch points), touch_max_points(), primary_pointer / any_pointer: what the
computer has, as the CSS media features pointer / any-pointer / hover / any-hover describe it.
Gestures
Touch_Gesture (pure: give it positions and times): touch_gesture_down / move / up / cancel and, every frame while a
finger is down, touch_gesture_tick return Tap, Pan (pan: how far the content follows), Pinch (scale about
pos, plus pan), Long_Press, Fling (velocity, px/s) or End. touch_fling(velocity, friction) gives where
a fling glides to and for how long.