OSDN Git Service

資材グラフの描画を高速化する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / SwipeScrollify.cs
1 // Copyright (C) 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Drawing;\r
17 using System.Runtime.InteropServices;\r
18 using System.Windows.Forms;\r
19 using static System.Math;\r
20 \r
21 namespace KancolleSniffer\r
22 {\r
23     public class SwipeScrollify\r
24     {\r
25         private readonly MouseFilter _filter;\r
26 \r
27         public SwipeScrollify()\r
28         {\r
29             _filter = new MouseFilter();\r
30             Application.AddMessageFilter(_filter);\r
31         }\r
32 \r
33         public void AddPanel(Panel panel)\r
34         {\r
35             var handler = new PanelHandler(panel);\r
36             _filter.MouseDown += handler.MouseDown;\r
37             _filter.MouseMove += handler.MouseMove;\r
38             _filter.MouseUp += handler.MouseUp;\r
39         }\r
40 \r
41         public void AddShipListPanel(ShipListPanel panel)\r
42         {\r
43             var handler = new ShipListPanelHandler(panel);\r
44             _filter.MouseDown += handler.MouseDown;\r
45             _filter.MouseMove += handler.MouseMove;\r
46             _filter.MouseUp += handler.MouseUp;\r
47         }\r
48 \r
49         public void AddTreeView(TreeView treeView)\r
50         {\r
51             var handler = new TreeViewHandler(treeView);\r
52             _filter.MouseDown += handler.MouseDown;\r
53             _filter.MouseMove += handler.MouseMove;\r
54             _filter.MouseUp += handler.MouseUp;\r
55         }\r
56 \r
57         private class MouseFilter : IMessageFilter\r
58         {\r
59             public delegate void MouseHandler(IntPtr handle, ref bool handled);\r
60 \r
61             public event MouseHandler MouseMove , MouseDown , MouseUp;\r
62 \r
63             // ReSharper disable InconsistentNaming\r
64             private const int WM_MOUSEMOVE = 0x0200;\r
65             private const int WM_LBUTTONDOWN = 0x0201;\r
66             private const int WM_LBUTTONUP = 0x0202;\r
67             // ReSharper restore InconsistentNaming\r
68 \r
69             public bool PreFilterMessage(ref Message m)\r
70             {\r
71                 var handled = false;\r
72                 switch (m.Msg)\r
73                 {\r
74                     case WM_LBUTTONDOWN:\r
75                         MouseDown?.Invoke(m.HWnd, ref handled);\r
76                         break;\r
77                     case WM_MOUSEMOVE:\r
78                         MouseMove?.Invoke(m.HWnd, ref handled);\r
79                         break;\r
80                     case WM_LBUTTONUP:\r
81                         MouseUp?.Invoke(m.HWnd, ref handled);\r
82                         break;\r
83                 }\r
84                 return handled;\r
85             }\r
86         }\r
87 \r
88         private class PanelHandler\r
89         {\r
90             private readonly Panel _panel;\r
91             private bool _touch;\r
92             private Point _mouseStart;\r
93             private Point _panelStart;\r
94             private Point _scrollStart;\r
95             private const int ScrollCount = 6;\r
96 \r
97             public PanelHandler(Panel panel)\r
98             {\r
99                 _panel = panel;\r
100             }\r
101 \r
102             public void MouseDown(IntPtr handle, ref bool handled)\r
103             {\r
104                 if (!_mouseStart.IsEmpty)\r
105                     return;\r
106                 if (!_panel.RectangleToScreen(_panel.ClientRectangle).Contains(Control.MousePosition))\r
107                     return;\r
108                 var found = false;\r
109                 for (var control = Control.FromHandle(handle); control != null; control = control.Parent)\r
110                 {\r
111                     if (control != _panel)\r
112                         continue;\r
113                     found = true;\r
114                     break;\r
115                 }\r
116                 if (!found)\r
117                     return;\r
118                 _mouseStart = _scrollStart = Control.MousePosition;\r
119                 _panelStart = _panel.AutoScrollPosition;\r
120             }\r
121 \r
122             public void MouseMove(IntPtr handle, ref bool handled)\r
123             {\r
124                 if (_mouseStart.IsEmpty)\r
125                     return;\r
126                 var cur = Control.MousePosition;\r
127                 var dx = cur.X - _mouseStart.X;\r
128                 var dy = cur.Y - _mouseStart.Y;\r
129                 if (!_touch)\r
130                 {\r
131                     if (!(Abs(dx) > ScrollCount || Abs(dy) > ScrollCount))\r
132                         return;\r
133                    _touch = true;\r
134                 }\r
135                 if (Abs(_scrollStart.X - cur.X) > ScrollCount || Abs(_scrollStart.Y - cur.Y) > ScrollCount)\r
136                 {\r
137                     _panel.AutoScrollPosition = new Point(-_panelStart.X - dx, -_panelStart.Y - dy);\r
138                     _scrollStart = cur;\r
139                 }\r
140             }\r
141 \r
142             public void MouseUp(IntPtr handle, ref bool handled)\r
143             {\r
144                 _touch = false;\r
145                 _mouseStart = _panelStart = Point.Empty;\r
146             }\r
147         }\r
148         private class ShipListPanelHandler\r
149         {\r
150             private readonly ShipListPanel _panel;\r
151             private bool _touch;\r
152             private Point _mouseStart;\r
153             private int _barStart = -1;\r
154             private Point _scrollStart;\r
155             private const int ScrollCount = ShipListPanel.LineHeight;\r
156 \r
157             public ShipListPanelHandler(ShipListPanel panel)\r
158             {\r
159                 _panel = panel;\r
160             }\r
161 \r
162             public void MouseDown(IntPtr handle, ref bool handled)\r
163             {\r
164                 if (!_mouseStart.IsEmpty)\r
165                     return;\r
166                 if (!_panel.RectangleToScreen(_panel.ClientRectangle).Contains(Control.MousePosition) ||\r
167                     _panel.ScrollBar.RectangleToScreen(_panel.ScrollBar.ClientRectangle).Contains(Control.MousePosition))\r
168                     return;\r
169                 var found = false;\r
170                 for (var control = Control.FromHandle(handle); control != null; control = control.Parent)\r
171                 {\r
172                     if (control != _panel)\r
173                         continue;\r
174                     found = true;\r
175                     break;\r
176                 }\r
177                 if (!found)\r
178                     return;\r
179                 _mouseStart = _scrollStart = Control.MousePosition;\r
180                 _barStart = _panel.ScrollBar.Value;\r
181             }\r
182 \r
183             public void MouseMove(IntPtr handle, ref bool handled)\r
184             {\r
185                 if (_mouseStart.IsEmpty)\r
186                     return;\r
187                 var cur = Control.MousePosition;\r
188                 var dy = cur.Y - _mouseStart.Y;\r
189                 if (!_touch)\r
190                 {\r
191                     if (Abs(dy) <= ScrollCount)\r
192                         return;\r
193                     _touch = true;\r
194                 }\r
195                 if (Abs(_scrollStart.Y - cur.Y) > ScrollCount)\r
196                 {\r
197                     var bar = _panel.ScrollBar;\r
198                     bar.Value = Max(0, Min(bar.Maximum - bar.LargeChange + 1, _barStart - dy / ScrollCount));\r
199                     _scrollStart = cur;\r
200                 }\r
201             }\r
202 \r
203             public void MouseUp(IntPtr handle, ref bool handled)\r
204             {\r
205                 _touch = false;\r
206                 _barStart = -1;\r
207                 _mouseStart = Point.Empty;\r
208             }\r
209         }\r
210 \r
211         private class TreeViewHandler\r
212         {\r
213             private readonly TreeView _treeView;\r
214             private bool _touch;\r
215             private Point _mouseStart;\r
216             private Point _panelStart;\r
217 \r
218             [DllImport("user32.dll")]\r
219             private static extern int GetScrollPos(IntPtr hWnd, int nBar);\r
220 \r
221             [DllImport("user32.dll")]\r
222             private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);\r
223 \r
224             // ReSharper disable InconsistentNaming\r
225             private const int GWL_STYLE = -16;\r
226             private const int WS_HSCROLL = 0x00100000;\r
227             private const int WS_VSCROLL = 0x00200000;\r
228             // ReSharper restore InconsistentNaming\r
229 \r
230             [DllImport("user32.dll")]\r
231             private static extern int GetWindowLong(IntPtr hWnd, int nIndex);\r
232 \r
233             public TreeViewHandler(TreeView treeView)\r
234             {\r
235                 _treeView = treeView;\r
236             }\r
237 \r
238             public void MouseDown(IntPtr handle, ref bool handled)\r
239             {\r
240                 if (!_mouseStart.IsEmpty)\r
241                     return;\r
242                 var control = Control.FromHandle(handle);\r
243                 if (control == null || control != _treeView)\r
244                     return;\r
245                 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))\r
246                     return;\r
247                 var loc = _treeView.HitTest(_treeView.PointToClient(Control.MousePosition)).Location;\r
248                 // アイテムをクリックするとWM_LBUTTONUPが来ないので避ける\r
249                 if (loc == TreeViewHitTestLocations.Label || loc == TreeViewHitTestLocations.Image)\r
250                     return;\r
251                 _mouseStart = Control.MousePosition;\r
252                 _panelStart = ScrollPosition();\r
253             }\r
254 \r
255             public void MouseMove(IntPtr handle, ref bool handled)\r
256             {\r
257                 if (_mouseStart.IsEmpty)\r
258                     return;\r
259                 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))\r
260                 {\r
261                     // TreeViewではうまく動かないので外に出たら止める\r
262                     _touch = false;\r
263                     _mouseStart = _panelStart = Point.Empty;\r
264                     return;\r
265                 }\r
266                 var cur = Control.MousePosition;\r
267                 var dx = cur.X - _mouseStart.X;\r
268                 var dy = cur.Y - _mouseStart.Y;\r
269                 var style = GetWindowLong(_treeView.Handle, GWL_STYLE);\r
270                 if (_touch)\r
271                 {\r
272                     _treeView.BeginUpdate();\r
273                     if ((style & WS_HSCROLL) != 0)\r
274                         SetScrollPos(_treeView.Handle, 0, _panelStart.X - dx, true);\r
275                     if ((style & WS_VSCROLL) != 0)\r
276                         SetScrollPos(_treeView.Handle, 1, _panelStart.Y - dy / _treeView.ItemHeight, true);\r
277                     _treeView.EndUpdate();\r
278                     handled = true;\r
279                 }\r
280                 else if (Abs(dx) > 5 || Abs(dy) > 5)\r
281                     _touch = true;\r
282             }\r
283 \r
284             public void MouseUp(IntPtr handle, ref bool handled)\r
285             {\r
286                 if (_touch && !_panelStart.IsEmpty && _panelStart != ScrollPosition())\r
287                     handled = true;\r
288                 _touch = false;\r
289                 _mouseStart = _panelStart = Point.Empty;\r
290             }\r
291 \r
292             private Point ScrollPosition()\r
293             {\r
294                 return new Point(GetScrollPos(_treeView.Handle, 0), GetScrollPos(_treeView.Handle, 1));\r
295             }\r
296         }\r
297     }\r
298 }