Ver Fonte

Changed drag area API to a hit-testing API.

There were several good arguments for this: it's how Windows works with
 WM_NCHITTEST, SDL doesn't need to manage a list of rects, it allows more
 control over the regions (how do you use rects to cleanly surround a circular
 button?), the callback can be more optimized than a iterating a list of
 rects, and you don't have to send an updated list of rects whenever the
 window resizes or layout changes.
Ryan C. Gordon há 11 anos atrás
pai
commit
98c03f391d

+ 1 - 1
.hgignore

@@ -55,7 +55,6 @@ test/loopwave
 test/testatomic
 test/testaudioinfo
 test/testautomation
-test/testdragareas
 test/testdraw2
 test/testerror
 test/testfile
@@ -64,6 +63,7 @@ test/testgesture
 test/testgl2
 test/testgles
 test/testhaptic
+test/testhittesting
 test/testiconv
 test/testime
 test/testintersections

+ 35 - 27
include/SDL_video.h

@@ -791,43 +791,51 @@ extern DECLSPEC int SDLCALL SDL_GetWindowGammaRamp(SDL_Window * window,
                                                    Uint16 * green,
                                                    Uint16 * blue);
 
+typedef enum
+{
+    SDL_HITTEST_NORMAL,  /**< Region is normal. No special properties. */
+    SDL_HITTEST_DRAGGABLE,  /**< Region can drag entire window. */
+    /* !!! FIXME: resize enums here. */
+} SDL_HitTestResult;
+
+typedef SDL_HitTestResult (SDLCALL *SDL_HitTest)(SDL_Window *win,
+                                                 const SDL_Point *area,
+                                                 void *data);
+
 /**
- *  \brief Define regions of a window that can be used to drag it.
+ *  \brief Provide a callback that decides if a window region has special properties.
  *
- *  Normally windows are dragged by decorations provided by the system
- *  window manager (usually, a title bar), but for some apps, it makes sense
- *  to drag them from somewhere else inside the window itself; for example,
- *  one might have a borderless window that wants to be draggable from any
- *  part, or simulate its own title bar, etc.
+ *  Normally windows are dragged and resized by decorations provided by the
+ *  system window manager (a title bar, borders, etc), but for some apps, it
+ *  makes sense to drag them from somewhere else inside the window itself; for
+ *  example, one might have a borderless window that wants to be draggable
+ *  from any part, or simulate its own title bar, etc.
  *
- *  This method designates pieces of a given window as "drag areas," which
- *  will move the window when the user drags with his mouse, as if she had
- *  used the titlebar.
- *
- *  You may specify multiple drag areas, disconnected or overlapping. This
- *  function accepts an array of rectangles. Each call to this function will
- *  replace any previously-defined drag areas. To disable drag areas on a
- *  window, call this function with a NULL array of zero elements.
- *
- *  Drag areas do not automatically resize. If your window changes dimensions
- *  you should plan to re-call this function with new drag areas if
- *  appropriate.
+ *  This function lets the app provide a callback that designates pieces of
+ *  a given window as special. This callback is run during event processing
+ *  if we need to tell the OS to treat a region of the window specially; the
+ *  use of this callback is known as "hit testing."
  *
  *  Mouse input may not be delivered to your application if it is within
- *  a drag area; the OS will often apply that input to moving the window and
- *  not deliver it to the application.
+ *  a special area; the OS will often apply that input to moving the window or
+ *  resizing the window and not deliver it to the application.
+ *
+ *  Specifying NULL for a callback disables hit-testing. Hit-testing is
+ *  disabled by default.
  *
  *  Platforms that don't support this functionality will return -1
- *  unconditionally, even if you're attempting to disable drag areas.
+ *  unconditionally, even if you're attempting to disable hit-testing.
+ *
+ *  Your callback may fire at any time.
  *
- *  \param window The window to set drag areas on.
- *  \param areas An array of SDL_Rects containing num_areas elements.
- *  \param num_areas The number of elements in the areas parameter.
+ *  \param window The window to set hit-testing on.
+ *  \param callback The callback to call when doing a hit-test.
+ *  \param callback_data An app-defined void pointer passed to the callback.
  *  \return 0 on success, -1 on error (including unsupported).
  */
-extern DECLSPEC int SDLCALL SDL_SetWindowDragAreas(SDL_Window * window,
-                                                   const SDL_Rect *areas,
-                                                   int num_areas);
+extern DECLSPEC int SDLCALL SDL_SetWindowHitTest(SDL_Window * window,
+                                                 SDL_HitTest callback,
+                                                 void *callback_data);
 
 /**
  *  \brief Destroy a window.

+ 1 - 1
src/dynapi/SDL_dynapi_overrides.h

@@ -580,4 +580,4 @@
 #define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL
 #define SDL_WinRTRunApp SDL_WinRTRunApp_REAL
 #define SDL_CaptureMouse SDL_CaptureMouse_REAL
-#define SDL_SetWindowDragAreas SDL_SetWindowDragAreas_REAL
+#define SDL_SetWindowHitTest SDL_SetWindowHitTest_REAL

+ 1 - 1
src/dynapi/SDL_dynapi_procs.h

@@ -613,4 +613,4 @@ SDL_DYNAPI_PROC(const char*,SDL_WinRTGetFSPathUTF8,(SDL_WinRT_Path a),(a),return
 SDL_DYNAPI_PROC(int,SDL_WinRTRunApp,(int a, char **b, void *c),(a,b,c),return)
 #endif
 SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)
-SDL_DYNAPI_PROC(int,SDL_SetWindowDragAreas,(SDL_Window *a, const SDL_Rect *b, int c),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_SetWindowHitTest,(SDL_Window *a, SDL_HitTest b, void *c),(a,b,c),return)

+ 4 - 4
src/video/SDL_sysvideo.h

@@ -97,8 +97,8 @@ struct SDL_Window
 
     SDL_WindowShaper *shaper;
 
-    int num_drag_areas;
-    SDL_Rect *drag_areas;
+    SDL_HitTest hit_test;
+    void *hit_test_data;
 
     SDL_WindowUserData *data;
 
@@ -264,8 +264,8 @@ struct SDL_VideoDevice
     /* MessageBox */
     int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);
 
-    /* Drag areas. Note that (areas) and (num_areas) are also copied to the SDL_Window for you after this call. */
-    int (*SetWindowDragAreas)(SDL_Window * window, const SDL_Rect *areas, int num_areas);
+    /* Hit-testing */
+    int (*SetWindowHitTest)(SDL_Window * window, SDL_bool enabled);
 
     /* * * */
     /* Data common to all drivers */

+ 9 - 24
src/video/SDL_video.c

@@ -1411,8 +1411,8 @@ SDL_RecreateWindow(SDL_Window * window, Uint32 flags)
         SDL_FreeSurface(icon);
     }
 
-    if (window->num_drag_areas > 0) {
-        _this->SetWindowDragAreas(window, window->drag_areas, window->num_drag_areas);
+    if (window->hit_test > 0) {
+        _this->SetWindowHitTest(window, SDL_TRUE);
     }
 
     SDL_FinishWindowCreation(window, flags);
@@ -2310,8 +2310,6 @@ SDL_DestroyWindow(SDL_Window * window)
         _this->windows = window->next;
     }
 
-    SDL_free(window->drag_areas);
-
     SDL_free(window);
 }
 
@@ -3388,33 +3386,20 @@ SDL_ShouldAllowTopmost(void)
 }
 
 int
-SDL_SetWindowDragAreas(SDL_Window * window, const SDL_Rect *_areas, int num_areas)
+SDL_SetWindowHitTest(SDL_Window * window, SDL_HitTest callback, void *userdata)
 {
-    SDL_Rect *areas = NULL;
-
     CHECK_WINDOW_MAGIC(window, -1);
 
-    if (!_this->SetWindowDragAreas) {
+    if (!_this->SetWindowHitTest) {
         return SDL_Unsupported();
-    }
-
-    if (num_areas > 0) {
-        const size_t len = sizeof (SDL_Rect) * num_areas;
-        areas = (SDL_Rect *) SDL_malloc(len);
-        if (!areas) {
-            return SDL_OutOfMemory();
-        }
-        SDL_memcpy(areas, _areas, len);
-    }
-
-    if (_this->SetWindowDragAreas(window, areas, num_areas) == -1) {
-        SDL_free(areas);
+    } else if (_this->SetWindowHitTest(window, callback != NULL) == -1) {
         return -1;
     }
 
-    SDL_free(window->drag_areas);
-    window->drag_areas = areas;
-    window->num_drag_areas = num_areas;
+    window->hit_test = callback;
+    window->hit_test_data = userdata;
+
+    return 0;
 }
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 1 - 1
src/video/cocoa/SDL_cocoavideo.m

@@ -108,7 +108,7 @@ Cocoa_CreateDevice(int devindex)
     device->SetWindowGrab = Cocoa_SetWindowGrab;
     device->DestroyWindow = Cocoa_DestroyWindow;
     device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
-    device->SetWindowDragAreas = Cocoa_SetWindowDragAreas;
+    device->SetWindowHitTest = Cocoa_SetWindowHitTest;
 
     device->shape_driver.CreateShaper = Cocoa_CreateShaper;
     device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;

+ 2 - 3
src/video/cocoa/SDL_cocoawindow.h

@@ -77,7 +77,7 @@ typedef enum
 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
 
 /* See if event is in a drag area, toggle on window dragging. */
--(BOOL) processDragArea:(NSEvent *)theEvent;
+-(BOOL) processHitTest:(NSEvent *)theEvent;
 
 /* Window event handling */
 -(void) mouseDown:(NSEvent *) theEvent;
@@ -119,7 +119,6 @@ struct SDL_WindowData
     SDL_bool inWindowMove;
     Cocoa_WindowListener *listener;
     struct SDL_VideoData *videodata;
-    NSView *dragarea;
 };
 
 extern int Cocoa_CreateWindow(_THIS, SDL_Window * window);
@@ -144,7 +143,7 @@ extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
 extern void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
 extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
 extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
-extern int Cocoa_SetWindowDragAreas(SDL_Window *window, const SDL_Rect *areas, int num_areas);
+extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
 
 #endif /* _SDL_cocoawindow_h */
 

+ 13 - 19
src/video/cocoa/SDL_cocoawindow.m

@@ -657,26 +657,20 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
     /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
 }
 
-- (BOOL)processDragArea:(NSEvent *)theEvent
+- (BOOL)processHitTest:(NSEvent *)theEvent
 {
-    const int num_areas = _data->window->num_drag_areas;
-
     SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
-    SDL_assert((num_areas > 0) || !isDragAreaRunning);
 
-    if (num_areas > 0) {  /* if no drag areas, skip this. */
-        int i;
+    if (_data->window->hit_test) {  /* if no hit-test, skip this. */
         const NSPoint location = [theEvent locationInWindow];
         const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
-        const SDL_Rect *areas = _data->window->drag_areas;
-        for (i = 0; i < num_areas; i++) {
-            if (SDL_PointInRect(&point, &areas[i])) {
-                if (!isDragAreaRunning) {
-                    isDragAreaRunning = YES;
-                    [_data->nswindow setMovableByWindowBackground:YES];
-                }
-                return YES;  /* started a new drag! */
+        const SDL_HitTestResult rc = _data->window->hit_test(_data->window, &point, _data->window->hit_test_data);
+        if (rc == SDL_HITTEST_DRAGGABLE) {
+            if (!isDragAreaRunning) {
+                isDragAreaRunning = YES;
+                [_data->nswindow setMovableByWindowBackground:YES];
             }
+            return YES;  /* dragging! */
         }
     }
 
@@ -686,14 +680,14 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
         return YES;  /* was dragging, drop event. */
     }
 
-    return NO;  /* not a drag area, carry on. */
+    return NO;  /* not a special area, carry on. */
 }
 
 - (void)mouseDown:(NSEvent *)theEvent
 {
     int button;
 
-    if ([self processDragArea:theEvent]) {
+    if ([self processHitTest:theEvent]) {
         return;  /* dragging, drop event. */
     }
 
@@ -735,7 +729,7 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
 {
     int button;
 
-    if ([self processDragArea:theEvent]) {
+    if ([self processHitTest:theEvent]) {
         return;  /* stopped dragging, drop event. */
     }
 
@@ -778,7 +772,7 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
     NSPoint point;
     int x, y;
 
-    if ([self processDragArea:theEvent]) {
+    if ([self processHitTest:theEvent]) {
         return;  /* dragging, drop event. */
     }
 
@@ -1599,7 +1593,7 @@ Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
 }
 
 int
-Cocoa_SetWindowDragAreas(SDL_Window * window, const SDL_Rect *areas, int num_areas)
+Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
 {
     return 0;  /* just succeed, the real work is done elsewhere. */
 }

+ 8 - 13
src/video/x11/SDL_x11events.c

@@ -303,21 +303,16 @@ InitiateWindowMove(_THIS, const SDL_WindowData *data, const SDL_Point *point)
 }
 
 static SDL_bool
-ProcessDragArea(_THIS, const SDL_WindowData *data, const XEvent *xev)
+ProcessHitTest(_THIS, const SDL_WindowData *data, const XEvent *xev)
 {
-    const SDL_Window *window = data->window;
-    const int num_areas = window->num_drag_areas;
+    SDL_Window *window = data->window;
 
-    if (num_areas > 0) {
+    if (window->hit_test) {
         const SDL_Point point = { xev->xbutton.x, xev->xbutton.y };
-        const SDL_Rect *areas = window->drag_areas;
-        int i;
-
-        for (i = 0; i < num_areas; i++) {
-            if (SDL_PointInRect(&point, &areas[i])) {
-                InitiateWindowMove(_this, data, &point);
-                return SDL_TRUE;  /* dragging, drop this event. */
-            }
+        const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
+        if (rc == SDL_HITTEST_DRAGGABLE) {
+            InitiateWindowMove(_this, data, &point);
+            return SDL_TRUE;  /* dragging, drop this event. */
         }
     }
 
@@ -762,7 +757,7 @@ X11_DispatchEvent(_THIS)
                 SDL_SendMouseWheel(data->window, 0, 0, ticks);
             } else {
                 if(xevent.xbutton.button == Button1) {
-                    if (ProcessDragArea(_this, data, &xevent)) {
+                    if (ProcessHitTest(_this, data, &xevent)) {
                         break;  /* don't pass this event on to app. */
                     }
                 }

+ 1 - 1
src/video/x11/SDL_x11video.c

@@ -457,7 +457,7 @@ X11_CreateDevice(int devindex)
     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
     device->GetWindowWMInfo = X11_GetWindowWMInfo;
-    device->SetWindowDragAreas = X11_SetWindowDragAreas;
+    device->SetWindowHitTest = X11_SetWindowHitTest;
 
     device->shape_driver.CreateShaper = X11_CreateShaper;
     device->shape_driver.SetWindowShape = X11_SetWindowShape;

+ 2 - 2
src/video/x11/SDL_x11window.c

@@ -1445,9 +1445,9 @@ X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
 }
 
 int
-X11_SetWindowDragAreas(SDL_Window *window, const SDL_Rect *areas, int num_areas)
+X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
 {
-    return 0; // nothing to do, will be handled in event handler
+    return 0;  /* just succeed, the real work is done elsewhere. */
 }
 
 #endif /* SDL_VIDEO_DRIVER_X11 */

+ 1 - 1
src/video/x11/SDL_x11window.h

@@ -93,7 +93,7 @@ extern void X11_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
 extern void X11_DestroyWindow(_THIS, SDL_Window * window);
 extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window,
                                     struct SDL_SysWMinfo *info);
-extern int X11_SetWindowDragAreas(SDL_Window *window, const SDL_Rect *areas, int num_areas);
+extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
 
 #endif /* _SDL_x11window_h */
 

+ 2 - 2
test/Makefile.in

@@ -12,7 +12,6 @@ TARGETS = \
 	loopwave$(EXE) \
 	testaudioinfo$(EXE) \
 	testautomation$(EXE) \
-	testdragareas$(EXE) \
 	testdraw2$(EXE) \
 	testdrawchessboard$(EXE) \
 	testdropfile$(EXE) \
@@ -24,6 +23,7 @@ TARGETS = \
 	testgles$(EXE) \
 	testgles2$(EXE) \
 	testhaptic$(EXE) \
+	testhittesting$(EXE) \
 	testrumble$(EXE) \
 	testhotplug$(EXE) \
 	testthread$(EXE) \
@@ -109,7 +109,7 @@ testintersections$(EXE): $(srcdir)/testintersections.c
 testrelative$(EXE): $(srcdir)/testrelative.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
-testdragareas$(EXE): $(srcdir)/testdragareas.c
+testhittesting$(EXE): $(srcdir)/testhittesting.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
 testdraw2$(EXE): $(srcdir)/testdraw2.c

+ 25 - 16
test/testdragareas.c → test/testhittesting.c

@@ -3,28 +3,42 @@
 
 /* !!! FIXME: rewrite this to be wired in to test framework. */
 
+const SDL_Rect drag_areas[] = {
+    { 20, 20, 100, 100 },
+    { 200, 70, 100, 100 },
+    { 400, 90, 100, 100 }
+};
+
+static const SDL_Rect *areas = drag_areas;
+static int numareas = SDL_arraysize(drag_areas);
+
+static SDL_HitTestResult
+hitTest(SDL_Window *window, const SDL_Point *pt, void *data)
+{
+    int i;
+    for (i = 0; i < numareas; i++) {
+        if (SDL_PointInRect(pt, &areas[i])) {
+            return SDL_HITTEST_DRAGGABLE;
+        }
+    }
+
+    return SDL_HITTEST_NORMAL;
+}
+
+
 int main(int argc, char **argv)
 {
     int done = 0;
     SDL_Window *window;
     SDL_Renderer *renderer;
 
-    const SDL_Rect drag_areas[] = {
-        { 20, 20, 100, 100 },
-        { 200, 70, 100, 100 },
-        { 400, 90, 100, 100 }
-    };
-
-    const SDL_Rect *areas = drag_areas;
-    int numareas = SDL_arraysize(drag_areas);
-
     /* !!! FIXME: check for errors. */
     SDL_Init(SDL_INIT_VIDEO);
     window = SDL_CreateWindow("Drag the red boxes", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_BORDERLESS);
     renderer = SDL_CreateRenderer(window, -1, 0);
 
-    if (SDL_SetWindowDragAreas(window, areas, numareas) == -1) {
-        fprintf(stderr, "Setting drag areas failed!\n");
+    if (SDL_SetWindowHitTest(window, hitTest, NULL) == -1) {
+        fprintf(stderr, "Enabling hit-testing failed!\n");
         SDL_Quit();
         return 1;
     }
@@ -69,11 +83,6 @@ int main(int argc, char **argv)
                             areas = NULL;
                             numareas = 0;
                         }
-                        if (SDL_SetWindowDragAreas(window, areas, numareas) == -1) {
-                            fprintf(stderr, "Setting drag areas failed!\n");
-                            SDL_Quit();
-                            return 1;
-                        }
                     }
                     break;