1 // Copyright (C) 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>
\r
3 // This program is part of KancolleSniffer.
\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify
\r
6 // it under the terms of the GNU General Public License as published by
\r
7 // the Free Software Foundation, either version 3 of the License, or
\r
8 // (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.
\r
19 using System.Drawing;
\r
20 using System.Runtime.InteropServices;
\r
21 using System.Windows.Forms;
\r
22 using static System.Math;
\r
24 namespace KancolleSniffer
\r
26 public class SwipeScrollify
\r
28 private readonly MouseFilter _filter;
\r
30 public SwipeScrollify()
\r
32 _filter = new MouseFilter();
\r
33 Application.AddMessageFilter(_filter);
\r
36 public void AddPanel(Panel panel)
\r
38 var handler = new PanelHandler(panel);
\r
39 _filter.MouseDown += handler.MouseDown;
\r
40 _filter.MouseMove += handler.MouseMove;
\r
41 _filter.MouseUp += handler.MouseUp;
\r
44 public void AddTreeView(TreeView treeView)
\r
46 var handler = new TreeViewHandler(treeView);
\r
47 _filter.MouseDown += handler.MouseDown;
\r
48 _filter.MouseMove += handler.MouseMove;
\r
49 _filter.MouseUp += handler.MouseUp;
\r
52 private class MouseFilter : IMessageFilter
\r
54 public delegate void MouseHandler(IntPtr handle, ref bool handled);
\r
56 public event MouseHandler MouseMove , MouseDown , MouseUp;
\r
58 // ReSharper disable InconsistentNaming
\r
59 private const int WM_MOUSEMOVE = 0x0200;
\r
60 private const int WM_LBUTTONDOWN = 0x0201;
\r
61 private const int WM_LBUTTONUP = 0x0202;
\r
62 // ReSharper restore InconsistentNaming
\r
64 public bool PreFilterMessage(ref Message m)
\r
66 var handled = false;
\r
69 case WM_LBUTTONDOWN:
\r
70 MouseDown?.Invoke(m.HWnd, ref handled);
\r
73 MouseMove?.Invoke(m.HWnd, ref handled);
\r
76 MouseUp?.Invoke(m.HWnd, ref handled);
\r
83 private class PanelHandler
\r
85 private readonly Panel _panel;
\r
86 private bool _touch;
\r
87 private Point _mouseStart;
\r
88 private Point _panelStart;
\r
90 public PanelHandler(Panel panel)
\r
95 public void MouseDown(IntPtr handle, ref bool handled)
\r
97 if (!_mouseStart.IsEmpty)
\r
99 if (!_panel.RectangleToScreen(_panel.ClientRectangle).Contains(Control.MousePosition))
\r
102 for (var control = Control.FromHandle(handle); control != null; control = control.Parent)
\r
104 if (control != _panel)
\r
111 _mouseStart = Control.MousePosition;
\r
112 _panelStart = _panel.AutoScrollPosition;
\r
115 public void MouseMove(IntPtr handle, ref bool handled)
\r
117 if (_mouseStart.IsEmpty)
\r
119 var cur = Control.MousePosition;
\r
120 var dx = cur.X - _mouseStart.X;
\r
121 var dy = cur.Y - _mouseStart.Y;
\r
123 _panel.AutoScrollPosition = new Point(-_panelStart.X - dx, -_panelStart.Y - dy);
\r
124 else if (Abs(dx) > 5 || Abs(dy) > 5)
\r
128 public void MouseUp(IntPtr handle, ref bool handled)
\r
130 if (_touch && !_panelStart.IsEmpty && _panelStart != _panel.AutoScrollPosition)
\r
133 _mouseStart = _panelStart = Point.Empty;
\r
137 private class TreeViewHandler
\r
139 private readonly TreeView _treeView;
\r
140 private bool _touch;
\r
141 private Point _mouseStart;
\r
142 private Point _panelStart;
\r
144 [DllImport("user32.dll")]
\r
145 private static extern int GetScrollPos(IntPtr hWnd, int nBar);
\r
147 [DllImport("user32.dll")]
\r
148 private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
\r
150 // ReSharper disable InconsistentNaming
\r
151 private const int GWL_STYLE = -16;
\r
152 private const int WS_HSCROLL = 0x00100000;
\r
153 private const int WS_VSCROLL = 0x00200000;
\r
154 // ReSharper restore InconsistentNaming
\r
156 [DllImport("user32.dll")]
\r
157 private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
\r
159 public TreeViewHandler(TreeView treeView)
\r
161 _treeView = treeView;
\r
164 public void MouseDown(IntPtr handle, ref bool handled)
\r
166 if (!_mouseStart.IsEmpty)
\r
168 var control = Control.FromHandle(handle);
\r
169 if (control == null || control != _treeView)
\r
171 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))
\r
173 var loc = _treeView.HitTest(_treeView.PointToClient(Control.MousePosition)).Location;
\r
174 // アイテムをクリックするとWM_LBUTTONUPが来ないので避ける
\r
175 if (loc == TreeViewHitTestLocations.Label || loc == TreeViewHitTestLocations.Image)
\r
177 _mouseStart = Control.MousePosition;
\r
178 _panelStart = ScrollPosition();
\r
181 public void MouseMove(IntPtr handle, ref bool handled)
\r
183 if (_mouseStart.IsEmpty)
\r
185 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))
\r
187 // TreeViewではうまく動かないので外に出たら止める
\r
189 _mouseStart = _panelStart = Point.Empty;
\r
192 var cur = Control.MousePosition;
\r
193 var dx = cur.X - _mouseStart.X;
\r
194 var dy = cur.Y - _mouseStart.Y;
\r
195 var style = GetWindowLong(_treeView.Handle, GWL_STYLE);
\r
198 _treeView.BeginUpdate();
\r
199 if ((style & WS_HSCROLL) != 0)
\r
200 SetScrollPos(_treeView.Handle, 0, _panelStart.X - dx, true);
\r
201 if ((style & WS_VSCROLL) != 0)
\r
202 SetScrollPos(_treeView.Handle, 1, _panelStart.Y - dy / _treeView.ItemHeight, true);
\r
203 _treeView.EndUpdate();
\r
206 else if (Abs(dx) > 5 || Abs(dy) > 5)
\r
210 public void MouseUp(IntPtr handle, ref bool handled)
\r
212 if (_touch && !_panelStart.IsEmpty && _panelStart != ScrollPosition())
\r
215 _mouseStart = _panelStart = Point.Empty;
\r
218 private Point ScrollPosition()
\r
220 return new Point(GetScrollPos(_treeView.Handle, 0), GetScrollPos(_treeView.Handle, 1));
\r