OSDN Git Service

IUpdatableをIUpdateContextとIUpdateTimerに分離する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / ListScroller.cs
1 // Copyright (C) 2019 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.Windows.Forms;\r
18 \r
19 namespace KancolleSniffer.View\r
20 {\r
21     public class ListScroller\r
22     {\r
23         private readonly Panel _panel;\r
24         private const int MarkWidth = 20;\r
25 \r
26         public int Padding { get; set; }\r
27 \r
28         public int Position { get; set; }\r
29 \r
30         public int Lines { get; set; }\r
31 \r
32         public int DataCount { get; set; }\r
33 \r
34         public event Action Update;\r
35 \r
36         public event Action StartScroll;\r
37 \r
38         public event Action EndScroll;\r
39 \r
40         public ListScroller(Panel panel, Control[] topLabels, Control[] bottomLabels)\r
41         {\r
42             _panel = panel;\r
43             panel.Paint += (obj, ev) => DrawMark();\r
44             SetTopEventHandler(topLabels);\r
45             SetBottomEventHandler(bottomLabels);\r
46         }\r
47 \r
48         public void SetTopEventHandler(Control[] top)\r
49         {\r
50             foreach (var label in top)\r
51             {\r
52                 label.MouseEnter += TopLineOnMouseEnter;\r
53                 label.MouseLeave += TopLineOnMouseLeave;\r
54             }\r
55             _topScrollRepeatTimer.Tick += (obj, e) => ScrollUp();\r
56         }\r
57 \r
58         public void SetBottomEventHandler(Control[] bottom)\r
59         {\r
60             foreach (var label in bottom)\r
61             {\r
62                 label.MouseEnter += BottomLineOnMouseEnter;\r
63                 label.MouseLeave += BottomLineOnMouseLeave;\r
64             }\r
65             _bottomScrollRepeatTimer.Tick += (obj, e) => ScrollDown();\r
66         }\r
67 \r
68         private readonly Timer _topScrollRepeatTimer = new Timer {Interval = 100};\r
69         private readonly Timer _bottomScrollRepeatTimer = new Timer {Interval = 100};\r
70 \r
71         private void TopLineOnMouseEnter(object sender, EventArgs e)\r
72         {\r
73             if (IsTop)\r
74                 return;\r
75             StartScroll?.Invoke();\r
76             _topScrollRepeatTimer.Start();\r
77         }\r
78 \r
79         private void ScrollUp()\r
80         {\r
81             if (IsTop)\r
82                 return;\r
83             Position--;\r
84             Update?.Invoke();\r
85         }\r
86 \r
87         private bool IsTop => Position == 0;\r
88 \r
89         private void TopLineOnMouseLeave(object sender, EventArgs e)\r
90         {\r
91             EndScroll?.Invoke();\r
92             _topScrollRepeatTimer.Stop();\r
93         }\r
94 \r
95         private void BottomLineOnMouseEnter(object sender, EventArgs e)\r
96         {\r
97             if (!NeedScroll)\r
98                 return;\r
99             StartScroll?.Invoke();\r
100             _bottomScrollRepeatTimer.Start();\r
101         }\r
102 \r
103         private void ScrollDown()\r
104         {\r
105             if (!NeedScroll)\r
106                 return;\r
107             Position++;\r
108             Update?.Invoke();\r
109         }\r
110 \r
111         private bool NeedScroll => Position + Lines < DataCount;\r
112 \r
113         private void BottomLineOnMouseLeave(object sender, EventArgs e)\r
114         {\r
115             EndScroll?.Invoke();\r
116             _bottomScrollRepeatTimer.Stop();\r
117         }\r
118 \r
119         public void DrawMark()\r
120         {\r
121             var halfOfWidth = _panel.Width * 0.5f;\r
122             var halfOfMark = Scaler.ScaleWidth(MarkWidth * 0.5f);\r
123             var left = halfOfWidth - halfOfMark;\r
124             var right = halfOfWidth + halfOfMark;\r
125             var center = halfOfWidth;\r
126             using (var g = _panel.CreateGraphics())\r
127             {\r
128                 DrawTopMark(g, left, right, center);\r
129                 DrawBottomMark(g, left, right, center);\r
130             }\r
131         }\r
132 \r
133         private void DrawTopMark(Graphics g, float left, float right, float center)\r
134         {\r
135             var brush = Position > 0 ? Brushes.Black : new SolidBrush(_panel.BackColor);\r
136             var top = -1;\r
137             var base_ = Padding - 1;\r
138             g.FillPolygon(brush,\r
139                 new[]\r
140                 {\r
141                     new PointF(left, base_), new PointF(right, base_), new PointF(center, top),\r
142                     new PointF(left, base_)\r
143                 });\r
144         }\r
145 \r
146         private void DrawBottomMark(Graphics g, float left, float right, float center)\r
147         {\r
148             var brush = Position + Lines < DataCount ? Brushes.Black : new SolidBrush(_panel.BackColor);\r
149             var top = _panel.Height - 2;\r
150             var base_ = _panel.Height - Padding - 1;\r
151             g.FillPolygon(brush,\r
152                 new[]\r
153                 {\r
154                     new PointF(left, base_), new PointF(right, base_), new PointF(center, top),\r
155                     new PointF(left, base_)\r
156                 });\r
157         }\r
158     }\r
159 }