Skip to content

Commit 4bc6e8e

Browse files
committed
[WIP] Change tool window docking behaviour.
Display a "shadow" over the area where a tool window will be docked and dock it when the mouse button is released rather than immediately docking it when the cursor enters the dock site.
1 parent 207536e commit 4bc6e8e

File tree

2 files changed

+199
-54
lines changed

2 files changed

+199
-54
lines changed

src/ToolDock.cpp

Lines changed: 181 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ REHex::ToolDock::ToolDock(wxWindow *parent):
5858
m_bottom_notebook = new ToolNotebook(this, wxID_ANY, wxNB_BOTTOM);
5959
m_bottom_notebook->Bind(wxEVT_LEFT_DOWN, &REHex::ToolDock::OnNotebookLeftDown, this);
6060
m_bottom_notebook->Hide();
61+
62+
m_dock_shadow = new DockShadow(this, wxRect(0, 0, 0, 0));
6163
}
6264

6365
void REHex::ToolDock::AddMainPanel(wxWindow *main_panel)
@@ -545,6 +547,37 @@ REHex::ToolPanel *REHex::ToolDock::FindToolByName(const std::string &name) const
545547
return tool;
546548
}
547549

550+
REHex::ToolDock::ToolNotebook *REHex::ToolDock::FindDockNotebook(const wxPoint &point, ToolNotebook *current_notebook)
551+
{
552+
ToolNotebook *dest_notebook = (ToolNotebook*)(FindChildByPoint(point));
553+
if(dest_notebook == NULL || dest_notebook != current_notebook)
554+
{
555+
wxPoint screen_point = ClientToScreen(point);
556+
557+
if(m_left_dock_site != NULL && m_left_dock_site->GetScreenRect().Contains(screen_point))
558+
{
559+
dest_notebook = m_left_notebook;
560+
}
561+
else if(m_right_dock_site != NULL && m_right_dock_site->GetScreenRect().Contains(screen_point))
562+
{
563+
dest_notebook = m_right_notebook;
564+
}
565+
else if(m_top_dock_site != NULL && m_top_dock_site->GetScreenRect().Contains(screen_point))
566+
{
567+
dest_notebook = m_top_notebook;
568+
}
569+
else if(m_bottom_dock_site != NULL && m_bottom_dock_site->GetScreenRect().Contains(screen_point))
570+
{
571+
dest_notebook = m_bottom_notebook;
572+
}
573+
else{
574+
dest_notebook = NULL;
575+
}
576+
}
577+
578+
return dest_notebook;
579+
}
580+
548581
void REHex::ToolDock::OnNotebookLeftDown(wxMouseEvent &event)
549582
{
550583
ToolNotebook *notebook = (ToolNotebook*)(event.GetEventObject());
@@ -580,6 +613,48 @@ void REHex::ToolDock::OnNotebookLeftDown(wxMouseEvent &event)
580613

581614
void REHex::ToolDock::OnLeftUp(wxMouseEvent &event)
582615
{
616+
if(m_drag_active)
617+
{
618+
ToolFrame *frame = FindFrameByTool(m_left_down_tool);
619+
ToolNotebook *notebook = FindNotebookByTool(m_left_down_tool);
620+
621+
assert(frame == NULL || notebook == NULL);
622+
623+
ToolNotebook *dest_notebook = FindDockNotebook(event.GetPosition(), notebook);
624+
625+
if(dest_notebook != NULL && dest_notebook != notebook)
626+
{
627+
if(notebook != NULL)
628+
{
629+
notebook->RemovePage(notebook->FindPage(m_left_down_tool));
630+
631+
if(notebook->GetPageCount() == 0)
632+
{
633+
notebook->Hide();
634+
}
635+
}
636+
637+
if(frame != NULL)
638+
{
639+
frame->GetSizer()->Detach(m_left_down_tool);
640+
}
641+
642+
m_left_down_tool->Reparent(dest_notebook);
643+
dest_notebook->AddPage(m_left_down_tool, m_left_down_tool->label(), true);
644+
645+
if(dest_notebook->GetPageCount() == 1)
646+
{
647+
ResetNotebookSize(dest_notebook);
648+
}
649+
650+
if(frame != NULL)
651+
{
652+
frame->Destroy();
653+
m_tool_frames.erase(m_left_down_tool);
654+
}
655+
}
656+
}
657+
583658
if(m_drag_pending || m_drag_active)
584659
{
585660
ReleaseMouse();
@@ -588,6 +663,7 @@ void REHex::ToolDock::OnLeftUp(wxMouseEvent &event)
588663
m_drag_active = false;
589664

590665
DestroyDockSites();
666+
m_dock_shadow->Hide();
591667
}
592668

593669
event.Skip();
@@ -601,6 +677,7 @@ void REHex::ToolDock::OnMouseCaptureLost(wxMouseCaptureLostEvent &event)
601677
m_drag_active = false;
602678

603679
DestroyDockSites();
680+
m_dock_shadow->Hide();
604681
}
605682
else{
606683
event.Skip();
@@ -631,67 +708,66 @@ void REHex::ToolDock::OnMotion(wxMouseEvent &event)
631708

632709
assert(frame == NULL || notebook == NULL);
633710

634-
ToolNotebook *dest_notebook = (ToolNotebook*)(FindChildByPoint(event.GetPosition()));
635-
if(dest_notebook == NULL || dest_notebook != notebook)
636-
{
637-
wxPoint screen_mouse_point = ClientToScreen(event.GetPosition());
638-
639-
if(m_left_dock_site != NULL && m_left_dock_site->GetScreenRect().Contains(screen_mouse_point))
640-
{
641-
dest_notebook = m_left_notebook;
642-
}
643-
else if(m_right_dock_site != NULL && m_right_dock_site->GetScreenRect().Contains(screen_mouse_point))
644-
{
645-
dest_notebook = m_right_notebook;
646-
}
647-
else if(m_top_dock_site != NULL && m_top_dock_site->GetScreenRect().Contains(screen_mouse_point))
648-
{
649-
dest_notebook = m_top_notebook;
650-
}
651-
else if(m_bottom_dock_site != NULL && m_bottom_dock_site->GetScreenRect().Contains(screen_mouse_point))
652-
{
653-
dest_notebook = m_bottom_notebook;
654-
}
655-
else{
656-
dest_notebook = NULL;
657-
}
658-
}
711+
ToolNotebook *dest_notebook = FindDockNotebook(event.GetPosition(), notebook);
659712

660713
if(dest_notebook != NULL)
661714
{
662715
if(dest_notebook != notebook)
663716
{
664-
if(notebook != NULL)
717+
if(dest_notebook->IsShown())
665718
{
666-
notebook->RemovePage(notebook->FindPage(m_left_down_tool));
719+
m_dock_shadow->Move(dest_notebook->GetScreenRect());
720+
m_dock_shadow->Show();
721+
}
722+
else{
723+
wxPoint client_base = ClientToScreen(wxPoint(0, 0));
724+
wxSize client_size = GetClientSize();
725+
726+
wxSize min_size = m_left_down_tool->GetEffectiveMinSize();
727+
wxSize best_size = m_left_down_tool->GetBestSize();
728+
729+
wxRect rect;
667730

668-
if(notebook->GetPageCount() == 0)
731+
if(dest_notebook == m_top_notebook || dest_notebook == m_bottom_notebook)
732+
{
733+
rect.width = client_size.GetWidth();
734+
rect.height = std::max(min_size.GetHeight(), best_size.GetHeight());
735+
}
736+
else{
737+
rect.width = std::max(min_size.GetWidth(), best_size.GetWidth());
738+
rect.height = client_size.GetHeight();
739+
}
740+
741+
if(dest_notebook == m_left_notebook || dest_notebook == m_top_notebook)
742+
{
743+
rect.x = client_base.x;
744+
rect.y = client_base.y;
745+
}
746+
else if(dest_notebook == m_right_notebook)
747+
{
748+
rect.x = client_base.x + client_size.GetWidth() - rect.width;
749+
rect.y = client_base.y;
750+
}
751+
else if(dest_notebook == m_bottom_notebook)
669752
{
670-
notebook->Hide();
753+
rect.x = client_base.x;
754+
rect.y = client_base.y + client_size.GetHeight() - rect.height;
671755
}
756+
757+
m_dock_shadow->Move(rect);
758+
m_dock_shadow->Show();
672759
}
673760

674-
if(frame != NULL)
675-
{
676-
frame->GetSizer()->Detach(m_left_down_tool);
677-
}
678-
679-
m_left_down_tool->Reparent(dest_notebook);
680-
dest_notebook->AddPage(m_left_down_tool, m_left_down_tool->label(), true);
681-
682-
if(dest_notebook->GetPageCount() == 1)
683-
{
684-
ResetNotebookSize(dest_notebook);
685-
}
686-
687-
if(frame != NULL)
688-
{
689-
frame->Destroy();
690-
m_tool_frames.erase(m_left_down_tool);
691-
}
761+
/* On Windows, the transparent wxPopupWindow isn't redrawn when the frame moves
762+
* around under it and I can't figure out a way to trigger an update that doesn't
763+
* result in the popup drawing over itself until its effectively opaque, so we just
764+
* hide the frame when the cursor is over a dock site on Windows.
765+
*/
766+
#ifdef _WIN32
767+
assert(frame != NULL);
768+
frame->Hide();
769+
#endif
692770
}
693-
694-
DestroyDockSites();
695771
}
696772
else{
697773
if(notebook != NULL)
@@ -706,21 +782,26 @@ void REHex::ToolDock::OnMotion(wxMouseEvent &event)
706782

707783
wxPoint frame_pos = ClientToScreen(event.GetPosition());
708784

709-
if(frame != NULL)
785+
if(frame == NULL)
710786
{
711-
frame->SetPosition(frame_pos);
712-
}
713-
else{
714787
frame = new ToolFrame(this, wxDefaultPosition, wxDefaultSize, m_left_down_tool);
715788
frame->SetPosition(frame_pos);
716-
frame->Show();
717789

718790
frame->Bind(wxEVT_CLOSE_WINDOW, &REHex::ToolDock::OnFrameClose, this);
719791

720792
m_tool_frames.emplace(m_left_down_tool, frame);
721793
}
722794

723795
SetupDockSites();
796+
m_dock_shadow->Hide();
797+
798+
frame->Show();
799+
}
800+
801+
if(frame != NULL)
802+
{
803+
wxPoint frame_pos = ClientToScreen(event.GetPosition());
804+
frame->SetPosition(frame_pos);
724805
}
725806
}
726807

@@ -1013,3 +1094,49 @@ REHex::ToolDock::DockSite::DockSite(wxWindow *parent, const wxBitmap &image, Anc
10131094

10141095
SetPosition(wxPoint(x, y));
10151096
}
1097+
1098+
BEGIN_EVENT_TABLE(REHex::ToolDock::DockShadow, wxPopupWindow)
1099+
EVT_PAINT(REHex::ToolDock::DockShadow::OnPaint)
1100+
END_EVENT_TABLE()
1101+
1102+
REHex::ToolDock::DockShadow::DockShadow(wxWindow *parent, const wxRect &rect):
1103+
wxPopupWindow()
1104+
{
1105+
SetBackgroundStyle(wxBG_STYLE_TRANSPARENT);
1106+
Create(parent);
1107+
1108+
Move(rect);
1109+
}
1110+
1111+
void REHex::ToolDock::DockShadow::Move(const wxRect &rect)
1112+
{
1113+
/* In wxGTK (at least), changing the wxPopupWindow size/position to one which overlaps
1114+
* with the previous position causes some weird bug where the client/paint area only covers
1115+
* the intersection between the new/old positions, returning the size/position to the
1116+
* previous position restores correct behaviour until you try moving it to overlap again.
1117+
*
1118+
* So we set the position miles off screen and *then* put it where we need it instead.
1119+
*/
1120+
#ifdef __WXGTK__
1121+
SetPosition(wxPoint(999999, 999999));
1122+
#endif
1123+
1124+
SetPosition(wxPoint(rect.x, rect.y));
1125+
SetSize(wxSize(rect.width, rect.height));
1126+
}
1127+
1128+
void REHex::ToolDock::DockShadow::OnPaint(wxPaintEvent &event)
1129+
{
1130+
wxPaintDC dc(this);
1131+
1132+
wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
1133+
if(gc)
1134+
{
1135+
wxSize client_size = GetClientSize();
1136+
1137+
gc->SetBrush(wxBrush(wxColour(0xF6, 0xD3, 0x2D, 100)));
1138+
gc->DrawRectangle(0, 0, client_size.GetWidth(), client_size.GetHeight());
1139+
1140+
delete gc;
1141+
}
1142+
}

src/ToolDock.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <string>
2323
#include <wx/config.h>
2424
#include <wx/event.h>
25+
#include <wx/graphics.h>
2526
#include <wx/notebook.h>
2627
#include <wx/popupwin.h>
2728
#include <wx/sizer.h>
@@ -131,6 +132,19 @@ namespace REHex
131132
DockSite(wxWindow *parent, const wxBitmap &image, Anchor anchor);
132133
};
133134

135+
class DockShadow: public wxPopupWindow
136+
{
137+
public:
138+
DockShadow(wxWindow *parent, const wxRect &rect);
139+
140+
void Move(const wxRect &rect);
141+
142+
private:
143+
void OnPaint(wxPaintEvent &event);
144+
145+
DECLARE_EVENT_TABLE()
146+
};
147+
134148
wxWindow *m_main_panel;
135149

136150
ToolNotebook *m_left_notebook;
@@ -150,10 +164,14 @@ namespace REHex
150164
DockSite *m_top_dock_site;
151165
DockSite *m_bottom_dock_site;
152166

167+
DockShadow *m_dock_shadow;
168+
153169
ToolFrame *FindFrameByTool(ToolPanel *tool);
154170
ToolNotebook *FindNotebookByTool(ToolPanel *tool);
155171
ToolPanel *FindToolByName(const std::string &name) const;
156172

173+
ToolNotebook *FindDockNotebook(const wxPoint &point, ToolNotebook *current);
174+
157175
void DestroyTool(ToolPanel *tool);
158176

159177
static void SaveToolsFromNotebook(wxConfig *config, ToolNotebook *notebook);

0 commit comments

Comments
 (0)