diff --git a/include/wx/dnd.h b/include/wx/dnd.h index c43083645b5f..5faed56c16c8 100644 --- a/include/wx/dnd.h +++ b/include/wx/dnd.h @@ -95,7 +95,6 @@ class WXDLLIMPEXP_CORE wxDropSourceBase // give the default feedback virtual bool GiveFeedback(wxDragResult WXUNUSED(effect)) { return false; } -protected: const wxCursor& GetCursor(wxDragResult res) const { if ( res == wxDragCopy ) @@ -105,7 +104,8 @@ class WXDLLIMPEXP_CORE wxDropSourceBase else return m_cursorStop; } - + +protected: // the data we're dragging wxDataObject *m_data; diff --git a/include/wx/generic/headerctrlg.h b/include/wx/generic/headerctrlg.h index 77da772496cb..ff47ea4be54f 100644 --- a/include/wx/generic/headerctrlg.h +++ b/include/wx/generic/headerctrlg.h @@ -52,6 +52,12 @@ class WXDLLIMPEXP_CORE wxHeaderCtrl : public wxHeaderCtrlBase private: + enum class Region{ + NoWhere, + LeftHalf, + RightHalf, + Separator + }; // implement base class pure virtuals virtual void DoSetCount(unsigned int count) wxOVERRIDE; virtual unsigned int DoGetCount() const wxOVERRIDE; @@ -96,11 +102,14 @@ class WXDLLIMPEXP_CORE wxHeaderCtrl : public wxHeaderCtrlBase // position is near the divider at the right end of this column (notice // that this means that we return column 0 even if the position is over // column 1 but close enough to the divider separating it from column 0) - unsigned int FindColumnAtPoint(int x, bool *onSeparator = NULL) const; + unsigned int FindColumnAtPoint(int x, Region& pos_region) const; // return the result of FindColumnAtPoint() if it is a valid column, // otherwise the index of the last (rightmost) displayed column - unsigned int FindColumnClosestToPoint(int xPhysical) const; + unsigned int FindColumnClosestToPoint(int xPhysical, Region& pos_region) const; + + unsigned int FindColumnAfter(const unsigned int column_idx) const; + unsigned int FindColumnBefore(const unsigned int column_idx) const; // return true if a drag resizing operation is currently in progress bool IsResizing() const; diff --git a/include/wx/osx/cocoa/private/dnd.h b/include/wx/osx/cocoa/private/dnd.h new file mode 100644 index 000000000000..b7955da86c62 --- /dev/null +++ b/include/wx/osx/cocoa/private/dnd.h @@ -0,0 +1,36 @@ +#include "wx/wxprec.h" + +#if wxUSE_DRAG_AND_DROP || wxUSE_CLIPBOARD + +#ifndef WX_PRECOMP +#include "wx/object.h" +#endif + +#include "wx/dnd.h" + +#include "wx/osx/private.h" +#include "wx/osx/private/datatransfer.h" + +@interface DropSourceDelegate : NSObject +{ + BOOL dragFinished; + int resultCode; + wxDropSource* impl; + + // Flags for drag and drop operations (wxDrag_* ). + int m_dragFlags; + NSImage* m_copy_cursor; + NSImage* m_move_cursor; + NSImage* m_none_cursor; +} + +- (void)setImplementation:(nonnull wxDropSource *)dropSource flags:(int)flags; +- (BOOL)finished; +- (NSDragOperation)code; +- (NSDragOperation)draggingSession:(nonnull NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context; +- (void)draggedImage:(nonnull NSImage *)anImage movedTo:(NSPoint)aPoint; +- (void)draggedImage:(nonnull NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; +- (nullable NSImage*)cursorForStatus:(wxDragResult)status; +@end + +#endif \ No newline at end of file diff --git a/include/wx/window.h b/include/wx/window.h index d7283458b1dd..dc5af35e976c 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -49,6 +49,12 @@ #define wxUSE_MENUS_NATIVE wxUSE_MENUS #endif // __WXUNIVERSAL__/!__WXUNIVERSAL__ +#ifdef __WXGTK__ +namespace CMExtension { + extern bool g_need_traditional_scrollbar; +} +#endif + // ---------------------------------------------------------------------------- // forward declarations // ---------------------------------------------------------------------------- diff --git a/src/common/popupcmn.cpp b/src/common/popupcmn.cpp index e959f1a77538..e2d522cf2771 100644 --- a/src/common/popupcmn.cpp +++ b/src/common/popupcmn.cpp @@ -451,19 +451,23 @@ void wxPopupTransientWindow::OnIdle(wxIdleEvent& event) if (IsShown() && m_child) { - // Store the last mouse position to minimize the number of calls to - // wxFindWindowAtPoint() which are quite expensive. + // Store the last mouse position static wxPoint s_posLast; const wxPoint pos = wxGetMousePosition(); if ( pos != s_posLast ) { s_posLast = pos; - - wxWindow* const winUnderMouse = wxFindWindowAtPoint(pos); + + // DO NOT use wxFindWindowAtPoint() because if there're multiple top level windows, + // it will 'Find the deepest window at the given mouse position in screen coordinates', + // which is not the right one for this logic. + auto screen_rect = GetScreenRect(); + const auto mouse_within_me = (pos.x >= screen_rect.GetTopLeft().x and pos.y >= screen_rect.GetTopLeft().y and + pos.x <= screen_rect.GetBottomRight().x and pos.y <= screen_rect.GetBottomRight().y); // We release the mouse capture while the mouse is inside the popup // itself to allow using it normally with the controls inside it. - if ( wxGetTopLevelParent(winUnderMouse) == this ) + if ( mouse_within_me ) { if ( m_child->HasCapture() ) { diff --git a/src/generic/headerctrlg.cpp b/src/generic/headerctrlg.cpp index 79a31a0eb285..26b373b01e4c 100644 --- a/src/generic/headerctrlg.cpp +++ b/src/generic/headerctrlg.cpp @@ -156,7 +156,7 @@ int wxHeaderCtrl::GetColStart(unsigned int idx) const if ( col.IsShown() ) pos += col.GetWidth(); } - + return pos; } @@ -167,7 +167,7 @@ int wxHeaderCtrl::GetColEnd(unsigned int idx) const return x + GetColumn(idx).GetWidth(); } -unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, bool *onSeparator) const +unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, Region& pos_region) const { int pos = 0; int xLogical = xPhysical - m_scrollOffset; @@ -178,7 +178,8 @@ unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, bool *onSeparator) c const wxHeaderColumn& col = GetColumn(idx); if ( col.IsHidden() ) continue; - + + const auto last_col_end = pos; pos += col.GetWidth(); // TODO: don't hardcode sensitivity @@ -188,33 +189,34 @@ unsigned int wxHeaderCtrl::FindColumnAtPoint(int xPhysical, bool *onSeparator) c // line separating it from the next column if ( col.IsResizeable() && abs(xLogical - pos) < separatorClickMargin ) { - if ( onSeparator ) - *onSeparator = true; + pos_region = Region::Separator; return idx; } // inside this column? - if ( xLogical < pos ) + if ( xLogical < pos && xLogical >= last_col_end) { - if ( onSeparator ) - *onSeparator = false; + if ( xLogical - last_col_end < pos - xLogical) + pos_region = Region::LeftHalf; + else + pos_region = Region::RightHalf; return idx; } } - if ( onSeparator ) - *onSeparator = false; + pos_region = Region::NoWhere; return COL_NONE; } -unsigned int wxHeaderCtrl::FindColumnClosestToPoint(int xPhysical) const +unsigned int wxHeaderCtrl::FindColumnClosestToPoint(int xPhysical, Region& pos_region) const { - const unsigned int colIndexAtPoint = FindColumnAtPoint(xPhysical); - + const unsigned int colIndexAtPoint = FindColumnAtPoint(xPhysical, pos_region); + // valid column found? if ( colIndexAtPoint != COL_NONE ) return colIndexAtPoint; - + + pos_region = Region::NoWhere; // if not, xPhysical must be beyond the rightmost column, so return its // index instead -- if we have it const unsigned int count = GetColumnCount(); @@ -224,6 +226,37 @@ unsigned int wxHeaderCtrl::FindColumnClosestToPoint(int xPhysical) const return m_colIndices[count - 1]; } +unsigned int wxHeaderCtrl::FindColumnAfter(const unsigned int column_idx) const{ + const unsigned count = GetColumnCount(); + auto after_idx = COL_NONE; + //auto target_column_found = false; + for ( unsigned n = 0; n < count; n++ ) + { + if (m_colIndices[n] == column_idx && n + 1 < count ){ + after_idx = m_colIndices[n + 1]; + break; + } + } + return after_idx; +} + +unsigned int wxHeaderCtrl::FindColumnBefore(const unsigned int column_idx) const{ + const unsigned count = GetColumnCount(); + auto before_idx = COL_NONE; + auto target_column_found = false; + for ( unsigned n = 0; n < count; n++ ) + { + if (m_colIndices[n] == column_idx){ + target_column_found = true; + break; + } + before_idx = m_colIndices[n]; + } + if (not target_column_found) + before_idx = COL_NONE; + return before_idx; +} + // ---------------------------------------------------------------------------- // wxHeaderCtrl repainting // ---------------------------------------------------------------------------- @@ -350,6 +383,7 @@ void wxHeaderCtrl::StartOrContinueResizing(unsigned int col, int xPhysical) //else: we had already done the above when we started } + RefreshColsAfter(col); } void wxHeaderCtrl::EndResizing(int xPhysical) @@ -388,14 +422,21 @@ void wxHeaderCtrl::UpdateReorderingMarker(int xPhysical) // and also a hint indicating where it is going to be inserted if it's // dropped now - unsigned int col = FindColumnClosestToPoint(xPhysical); + auto hover_region = Region::NoWhere; + unsigned int col = FindColumnClosestToPoint(xPhysical, hover_region); if ( col != COL_NONE ) { static const int DROP_MARKER_WIDTH = 4; dc.SetBrush(*wxBLUE); - dc.DrawRectangle(GetColEnd(col) - DROP_MARKER_WIDTH/2, 0, - DROP_MARKER_WIDTH, y); + if (hover_region == Region::LeftHalf){ + dc.DrawRectangle(GetColStart(col) - DROP_MARKER_WIDTH/2, 0, + DROP_MARKER_WIDTH, y); + } + else if (hover_region != Region::NoWhere){ + dc.DrawRectangle(GetColEnd(col) - DROP_MARKER_WIDTH/2, 0, + DROP_MARKER_WIDTH, y); + } } } @@ -431,10 +472,39 @@ bool wxHeaderCtrl::EndReordering(int xPhysical) ReleaseMouse(); const int colOld = m_colBeingReordered; - const unsigned colNew = FindColumnClosestToPoint(xPhysical); + auto dropped_region = Region::NoWhere; + unsigned colNew = FindColumnClosestToPoint(xPhysical, dropped_region); m_colBeingReordered = COL_NONE; - + auto reg_str = std::string{}; + switch(dropped_region){ + case Region::NoWhere: + reg_str = "NoWhere"; + break; + case Region::LeftHalf: + reg_str = "LeftHalf"; + break; + case Region::RightHalf: + reg_str = "RightHalf"; + break; + case Region::Separator: + reg_str = "Separator"; + break; + } + //printf("Dropped to col %d, region : %s\n", colNew, reg_str.c_str()); + // The actual dropped pos should not simply be colNew, it should also depends on + // which region the user dropped in. + // if the user dropped the col on the RightHalf, the colNew should the one next to it on the right. + auto located_by_previous_col = false; + if ((dropped_region == Region::RightHalf || dropped_region == Region::Separator) && colNew != COL_NONE){ + //printf("Looking for the next column pos to inserted for col %d\n", colNew); + auto nextColumn = FindColumnAfter(colNew); + if (nextColumn != COL_NONE){ + //printf("Next col for col %d is %d\n", colNew, nextColumn); + colNew = nextColumn; + located_by_previous_col = true; + } + } // mouse drag must be longer than min distance m_dragOffset if ( xPhysical - GetColStart(colOld) == m_dragOffset ) { @@ -446,22 +516,45 @@ bool wxHeaderCtrl::EndReordering(int xPhysical) { return false; } - - if ( static_cast(colNew) != colOld ) + if ( static_cast(colNew) != colOld && dropped_region != Region::NoWhere ) { wxHeaderCtrlEvent event(wxEVT_HEADER_END_REORDER, GetId()); event.SetEventObject(this); event.SetColumn(colOld); - - const unsigned pos = GetColumnPos(colNew); - event.SetNewOrder(pos); - - if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() ) - { - // do reorder the columns - DoMoveCol(colOld, pos); + auto new_pos = GetColumnPos(colNew); + auto old_pos = GetColumnPos(colOld); + // when the user drag one col from left-to-right(i.e. from low pos to higher one), + // the actual pos to dropped should be the one just before colNew, i.e. the one on the left hand side of colNew. + auto move_left = false; + if (old_pos < new_pos) { + // the last column is a bit special, we should consider it differently. + if (new_pos != GetColumnCount() - 1 || located_by_previous_col || dropped_region == Region::LeftHalf){ + colNew = FindColumnBefore(colNew); + assert(colNew != COL_NONE); + new_pos = GetColumnPos(colNew); + } + move_left = true; + } + // Simulate the reorder before we actually accept it. + // Why??? + // ToDo: better code comments to explain the logic, a diagram should be better + auto new_colIndices = m_colIndices; + MoveColumnInOrderArray(new_colIndices, colOld, new_pos); + auto old_after_pos = new_colIndices.Index(colNew); + auto new_after_pos = new_colIndices.Index(colOld); + if (old_after_pos != old_pos || new_after_pos != new_pos || move_left || old_pos > new_pos){ + event.SetNewOrder(new_pos); + + //printf("Move col %d to %d, pos to %d\n", colOld, colNew, new_pos); + if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() ) + { + // do reorder the columns + DoMoveCol(colOld, new_pos); + } } } + //else + //printf("ColNew and ColOld the same, do not reordering.\n"); // whether we moved the column or not, the user did move the mouse and so // did try to do it so return true @@ -650,10 +743,10 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) // find if the event is over a column at all - bool onSeparator; + Region mouse_region = Region::NoWhere; const unsigned col = mevent.Leaving() - ? (onSeparator = false, COL_NONE) - : FindColumnAtPoint(xPhysical, &onSeparator); + ? COL_NONE + : FindColumnAtPoint(xPhysical, mouse_region); // update the highlighted column if it changed @@ -669,7 +762,7 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) // update mouse cursor as it moves around if ( mevent.Moving() ) { - SetCursor(onSeparator ? wxCursor(wxCURSOR_SIZEWE) : wxNullCursor); + SetCursor(mouse_region == Region::Separator ? wxCursor(wxCURSOR_SIZEWE) : wxNullCursor); return; } @@ -681,7 +774,7 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) // enter various dragging modes on left mouse press if ( mevent.LeftDown() ) { - if ( onSeparator ) + if ( mouse_region == Region::Separator ) { // start resizing the column wxASSERT_MSG( !IsResizing(), "reentering column resize mode?" ); @@ -711,7 +804,7 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) { case wxMOUSE_BTN_LEFT: // treat left double clicks on separator specially - if ( onSeparator && dblclk ) + if ( mouse_region == Region::Separator && dblclk ) { evtType = wxEVT_HEADER_SEPARATOR_DCLICK; m_wasSeparatorDClick = true; diff --git a/src/gtk/dnd.cpp b/src/gtk/dnd.cpp index 1a00bb67c346..619f0567f132 100644 --- a/src/gtk/dnd.cpp +++ b/src/gtk/dnd.cpp @@ -852,13 +852,11 @@ wxDragResult wxDropSource::DoDragDrop(int flags) if (g_blockEventsOnDrag) return wxDragNone; +#if 0 // behaves bad for Coremail Air Client, need to investigate whether the early exiting is correct // don't start dragging if no button is down if (g_lastButtonNumber == 0) return wxDragNone; - - // we can only start a drag after a mouse event - if (g_lastMouseEvent == NULL) - return wxDragNone; +#endif GTKConnectDragSignals(); wxON_BLOCK_EXIT_OBJ0(*this, wxDropSource::GTKDisconnectDragSignals); diff --git a/src/gtk/evtloop.cpp b/src/gtk/evtloop.cpp index eb9751c85a30..12f10a2c1d11 100644 --- a/src/gtk/evtloop.cpp +++ b/src/gtk/evtloop.cpp @@ -365,22 +365,29 @@ static void wxgtk_main_do_event(GdkEvent* event, void* data) } void wxGUIEventLoop::DoYieldFor(long eventsToProcess) -{ - // temporarily replace the global GDK event handler with our function, which - // categorizes the events and using m_eventsToProcessInsideYield decides - // if an event should be processed immediately or not - // NOTE: this approach is better than using gdk_display_get_event() because - // gtk_main_iteration() does more than just calling gdk_display_get_event() - // and then call gtk_main_do_event()! - // In particular in this way we also process input from sources like - // GIOChannels (this is needed for e.g. wxGUIAppTraits::WaitForChild). - gdk_event_handler_set(wxgtk_main_do_event, this, NULL); - while (Pending()) // avoid false positives from our idle source - gtk_main_iteration(); - - wxGCC_WARNING_SUPPRESS_CAST_FUNCTION_TYPE() - gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL); - wxGCC_WARNING_RESTORE_CAST_FUNCTION_TYPE() +{ + // DO NOT replace the global GDK event handler with our 'wxgtk_main_do_event'. + // Because this trick rely on one uncertain assumption: + // No one besides us, had done gdk_event_handler_set() already. + // In most case, this might be true. + // But a single exception can destroy this trick completely. + // For example, when we want to embed CEF, Chromium will use a custom gdk_event_handler too. + // This trick will render all CEF browser window/view no longer usable. + // GTK3 should be blamed for not offering any gdk_event_handler_get() or similar stuff for a safe trick here. + // In GTK4, there's a more sane way to get the job done. + // So let's do it in a more conservative way. + // If there're GdkEvents, we handle them via 'wxgtk_main_do_event', + // all other events should be handle by one gtk_main_iteration(). + // I'm not sure whether this is really okay, but it seems a nicer and less intrusive way to do things. + while(Pending()){ + auto gdk_event_ = gdk_event_get(); + if (gdk_event_ != nullptr){ + wxgtk_main_do_event(gdk_event_, this); + gdk_event_free(gdk_event_); + } + else + gtk_main_iteration(); + } wxEventLoopBase::DoYieldFor(eventsToProcess); diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 2730674e22e7..dfa3d1b2800e 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -72,6 +72,10 @@ typedef guint KeySym; #define PANGO_VERSION_CHECK(a,b,c) 0 #endif +namespace CMExtension { + bool g_need_traditional_scrollbar = false; +} + //----------------------------------------------------------------------------- // documentation on internals //----------------------------------------------------------------------------- @@ -2273,7 +2277,7 @@ gtk_window_leave_callback( GtkWidget*, win->GTKUpdateCursor(); // Event was emitted after an ungrab - if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE; + if (gdk_event->mode != GDK_CROSSING_NORMAL && gdk_event->mode != GDK_CROSSING_GRAB && gdk_event->mode != GDK_CROSSING_GTK_GRAB) return FALSE; wxMouseEvent event( wxEVT_LEAVE_WINDOW ); InitMouseEvent(win, event, gdk_event); @@ -2764,6 +2768,9 @@ void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget* view) m_widget = gtk_scrolled_window_new( NULL, NULL ); GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget); + + if (CMExtension::g_need_traditional_scrollbar) + g_object_set(scrolledWindow, "overlay-scrolling", false, NULL); // There is a conflict with default bindings at GTK+ // level between scrolled windows and notebooks both of which want to use @@ -4859,7 +4866,15 @@ void wxWindowGTK::SetFocus() // But avoid activating if tlw is not yet shown, as that will // cause it to be immediately shown. GtkWidget* tlw = gtk_widget_get_ancestor(m_widget, GTK_TYPE_WINDOW); - if (tlw && gtk_widget_get_visible(tlw) && !gtk_window_is_active(GTK_WINDOW(tlw))) + if (tlw && gtk_widget_get_visible(tlw)) + // we don't need to consider whether the tlw is active or not, + // for these two reasons: + // 1. if the gtk_window_is_active(tlw), gtk_window_preset() don't harm anyway; + // 2. when foreign XWindow embedded(not via GtkPlug), maybe use XWindow controlled by GDK as their parent, + // for example, a CEF browser window(which is now an XWindow) take ours as parent, + // our tlw active state maybe altered(when CEF window take the keyboard focus) and unknown to us. + // i.e. gtk_window_is_active(tlw) may still true while the focus is already taken by the CEF window. + // we need to force to bring up our tlw to the user at this moment. gtk_window_present(GTK_WINDOW(tlw)); GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget; diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 8646f9b822f6..d60a824e168c 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -2934,52 +2934,17 @@ static bool AlphaBlt(wxMSWDCImpl* dcDst, const wxBitmap& bmpDst = dcDst->GetSelectedBitmap(); if ( bmpDst.IsOk() && !bmpDst.HasAlpha() && bmpDst.GetDepth() == 32 ) { - // We need to deselect the bitmap from the memory DC it is - // currently selected into before modifying it. - wxBitmap bmpOld = bmpDst; - dcDst->DoSelect(wxNullBitmap); + wxBitmap bmp(dstWidth, dstHeight, 32); + wxMemoryDC dc(bmp); - // Notice the extra block: we must destroy wxAlphaPixelData - // before selecting the bitmap into the DC again. - { - // Since drawn bitmap can only partially overlap - // with destination bitmap we need to calculate - // efective drawing area location. - const wxRect rectDst(bmpOld.GetSize()); - const wxRect rectDrawn(x, y, dstWidth, dstHeight); - const wxRect r = rectDrawn.Intersect(rectDst); - - wxAlphaPixelData data(bmpOld); - if ( data ) - { - wxAlphaPixelData::Iterator p(data); - - p.Offset(data, r.GetLeft(), r.GetTop()); - for ( int old_y = 0; old_y < r.GetHeight(); old_y++ ) - { - wxAlphaPixelData::Iterator rowStart = p; - for ( int old_x = 0; old_x < r.GetWidth(); old_x++ ) - { - // We choose to use wxALPHA_TRANSPARENT instead - // of perhaps more logical wxALPHA_OPAQUE here - // to ensure that the bitmap remains the same - // as before, i.e. without any alpha at all. - p.Alpha() = wxALPHA_TRANSPARENT; - ++p; - } - - p = rowStart; - p.OffsetY(data, 1); - } - } - } - - // Using wxAlphaPixelData sets the internal "has alpha" flag - // which is usually what we need, but in this particular case - // we use it to get rid of alpha, not set it, so reset it back. - bmpOld.ResetAlpha(); - - dcDst->DoSelect(bmpOld); + // Fetch the content of the destination area into the temporary buffer. + wxRect r(x, y, dstWidth, dstHeight); + if (bmpDst.GetWidth() < x + dstWidth || bmpDst.GetHeight() < y + dstHeight) + return true; + dc.DrawBitmap(dcDst->DoGetAsBitmap(&r), 0, 0); + // Drawing the source over the temporary buffer. + dcDst->DoBlit(x, y, dstWidth, dstHeight, &dc, 0, 0); + return true; } #endif // wxHAS_RAW_BITMAP diff --git a/src/osx/cocoa/dnd.mm b/src/osx/cocoa/dnd.mm index d8786cc2de76..a652894a0d37 100644 --- a/src/osx/cocoa/dnd.mm +++ b/src/osx/cocoa/dnd.mm @@ -17,6 +17,7 @@ #endif #include "wx/dnd.h" +#include "wx/osx/cocoa/private/dnd.h" #include "wx/clipbrd.h" #include "wx/filename.h" @@ -268,24 +269,6 @@ wxDragResult NSDragOperationToWxDragResult(NSDragOperation code) return wxDragNone; } -@interface DropSourceDelegate : NSObject -{ - BOOL dragFinished; - int resultCode; - wxDropSource* impl; - - // Flags for drag and drop operations (wxDrag_* ). - int m_dragFlags; -} - -- (void)setImplementation:(wxDropSource *)dropSource flags:(int)flags; -- (BOOL)finished; -- (NSDragOperation)code; -- (NSDragOperation)draggingSession:(nonnull NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context; -- (void)draggedImage:(NSImage *)anImage movedTo:(NSPoint)aPoint; -- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; -@end - @implementation DropSourceDelegate - (id)init @@ -296,6 +279,9 @@ - (id)init resultCode = NSDragOperationNone; impl = 0; m_dragFlags = wxDrag_CopyOnly; + m_copy_cursor = nil; + m_move_cursor = nil; + m_none_cursor = nil; } return self; } @@ -397,6 +383,42 @@ - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDra dragFinished = YES; } +- (NSImage*)cursorForStatus:(wxDragResult)status +{ + NSImage* cursor_img = nil; + switch(status){ + case wxDragCopy: + cursor_img = m_copy_cursor; + break; + case wxDragMove: + cursor_img = m_move_cursor; + break; + default: + cursor_img = m_none_cursor; + break; + } + if (cursor_img != nil) + return cursor_img; + + wxCursor indicate_cursor = impl->GetCursor(status); + + if (indicate_cursor.IsOk()) { + NSCursor *cursor = (NSCursor*)indicate_cursor.GetHCURSOR(); + cursor_img = [[cursor image] retain]; + switch(status){ + case wxDragCopy: + m_copy_cursor = cursor_img; + break; + case wxDragMove: + m_move_cursor = cursor_img; + break; + default: + m_none_cursor = cursor_img; + break; + } + } + return cursor_img; +} @end wxDropTarget::wxDropTarget( wxDataObject *data ) @@ -486,17 +508,35 @@ - (nullable id)pasteboardPropertyListForType:(nonnull NSPasteboardType)type wxDragResult wxDropSource::DoDragDrop(int flags) { wxASSERT_MSG( m_data, wxT("Drop source: no data") ); - + static bool g_in_dnd = false; + wxDragResult result = wxDragNone; if ((m_data == NULL) || (m_data->GetFormatCount() == 0)) return result; - + + if (g_in_dnd) + return wxDragNone; + + g_in_dnd = true; NSView* view = m_window->GetPeer()->GetWXWidget(); if (view) { NSEvent* theEvent = (NSEvent*)wxTheApp->MacGetCurrentEvent(); - wxASSERT_MSG(theEvent, "DoDragDrop must be called in response to a mouse down or drag event."); - + // relax the constraint set for DoDragDrop(). + // if the user start DnD something from a frame rendered by CEF or similar framework, it should be perfectly valid. + if (theEvent == nil){ + NSPoint mouse_location = [NSEvent mouseLocation]; + theEvent = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDragged + location:mouse_location + modifierFlags:0 + timestamp: 0 + windowNumber: [NSWindow windowNumberAtPoint:mouse_location belowWindowWithWindowNumber:0] + context:nil + eventNumber: 0 + clickCount: 0 + pressure: 1.0]; + } + gCurrentSource = this; DropSourceDelegate* delegate = [[DropSourceDelegate alloc] init]; @@ -555,8 +595,8 @@ - (nullable id)pasteboardPropertyListForType:(nonnull NSPasteboardType)type gCurrentSource = NULL; } - - + + g_in_dnd = false; return result; } diff --git a/src/osx/cocoa/evtloop.mm b/src/osx/cocoa/evtloop.mm index 8820d597012f..4831de0765f4 100644 --- a/src/osx/cocoa/evtloop.mm +++ b/src/osx/cocoa/evtloop.mm @@ -380,7 +380,7 @@ static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat) NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0.0, 0.0) modifierFlags:0 - timestamp:0 + timestamp:[[NSProcessInfo processInfo] systemUptime] windowNumber:0 context:nil subtype:0 data1:0 data2:0]; @@ -434,7 +434,15 @@ static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat) if ( m_modalWindow ) { BeginModalSession(m_modalWindow); - wxCFEventLoop::OSXDoRun(); + while(true){ + try { + wxCFEventLoop::OSXDoRun(); + break; + } + catch(...){ + printf("wxModalEventLoop::OSXDoRun caught unknown exception\n"); + } + } EndModalSession(); } else diff --git a/src/osx/cocoa/utils.mm b/src/osx/cocoa/utils.mm index 66d663325f83..81ce3a686313 100644 --- a/src/osx/cocoa/utils.mm +++ b/src/osx/cocoa/utils.mm @@ -421,7 +421,7 @@ - (void)sendEvent:(NSEvent *)anEvent NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0.0, 0.0) modifierFlags:0 - timestamp:0 + timestamp:[[NSProcessInfo processInfo] systemUptime] windowNumber:0 context:nil subtype:0 data1:0 data2:0]; diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 6e5256f35763..e47b1aaf3f79 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -37,6 +37,9 @@ #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" #include "wx/clipbrd.h" + #ifdef __WXOSX_MAC__ + #include "wx/osx/cocoa/private/dnd.h" + #endif #endif #if wxUSE_TOOLTIPS @@ -1374,6 +1377,22 @@ When dragging the bottom part of the DND sample ("Drag text from here!") default : break; } + if (!entered){ + NSView* the_view = viewImpl->GetWXWidget(); + [sender enumerateDraggingItemsWithOptions:NSDraggingItemEnumerationConcurrent forView:the_view + classes:[NSArray arrayWithObject:[NSPasteboardItem class]] searchOptions:nil + usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) { + wxUnusedVar(idx); + NSRect theFrame = draggingItem.draggingFrame; + DropSourceDelegate* drop_src_delegate = sender.draggingSource; + NSImage* newDragImage = [drop_src_delegate cursorForStatus:result]; + if (newDragImage != nil){ + NSRect newFrame = NSMakeRect(theFrame.origin.x, theFrame.origin.y, newDragImage.size.width, newDragImage.size.height); + [draggingItem setDraggingFrame:newFrame contents:newDragImage]; + } + *stop = NO; + }]; + } return nsresult; } diff --git a/src/osx/webview_webkit.mm b/src/osx/webview_webkit.mm index bd8cadd53404..c40a22a73e9e 100644 --- a/src/osx/webview_webkit.mm +++ b/src/osx/webview_webkit.mm @@ -618,6 +618,10 @@ -(id)validRequestorForSendType:(NSString*)sendType returnType:(NSString*)returnT - (BOOL)performKeyEquivalent:(NSEvent *)event { + if (self.window.firstResponder != self) + { + return NO; + } if ([event modifierFlags] & NSCommandKeyMask) { switch ([event.characters characterAtIndex:0]) diff --git a/src/ribbon/buttonbar.cpp b/src/ribbon/buttonbar.cpp index 98b559538e54..6f4aad921043 100644 --- a/src/ribbon/buttonbar.cpp +++ b/src/ribbon/buttonbar.cpp @@ -1066,7 +1066,6 @@ void wxRibbonButtonBar::MakeLayouts() // small buttons small, stacked vertically wxRibbonButtonBarLayout* layout = new wxRibbonButtonBarLayout; wxPoint cursor(0, 0); - layout->overall_size.SetHeight(0); for(btn_i = 0; btn_i < btn_count; ++btn_i) { wxRibbonButtonBarButtonBase* button = m_buttons.Item(btn_i); @@ -1102,8 +1101,7 @@ void wxRibbonButtonBar::MakeLayouts() } layout->buttons.Add(instance); } - layout->overall_size.SetHeight(available_height); - layout->overall_size.SetWidth(cursor.x + stacked_width); + layout->CalculateOverallSize(); m_layouts.Add(layout); } if(btn_count >= 2)