|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // 独自スクロールバー
9: //
10:
11: // GTK のスクロールバーが色々残念なので、独自コントロールを作る。
12: //
13: // pos、thumbsize、range、pagesize は wxScrollBar のそれと同じなのでそちら参照。
14: // 例えば全体で100要素あるうち画面に10要素分だけ表示できる構成の場合、
15: // range = 100、thumbsize = 10 となる。表示位置は、一番上(左)の要素から表示
16: // している場合は pos = 0、一番下(右)の要素まで表示している場合は pos = 90。
17: //
18: // 0 10 90 100
19: // +---+-----------------+---+
20: //
21: // ピクセル座標では、上(左)に 2ピクセル、下(右)に1ピクセルのフチがある。
22: // そのため range として使える領域は bitmap.GetHeight() (または GetWidth())
23: // から 3 引いたサイズになる。
24: //
25: // 左フチ つまみ 右フチ
26: // +--+ ●----○ ++
27: // 0 1 p1 p2 h-1
28: // 明□□□□□□□□□明
29: // 明□□■■ □□□□明
30: // 明□□■■ □□□□明
31: // 明□□ □□□□明
32: // 明明明明明明明明明明明
33: //
34: // 明明明明明 0 <- 上フチ
35: // □□□□明 1 <- 上フチ
36: // □□□□明
37: // □■■ 明 p1 ●
38: // □■■ 明 | つまみ
39: // □ 明 |
40: // □□□□明 p2 ○
41: // □□□□明
42: // □□□□明
43: // 明明明明明 h-1 <- 下フチ
44: //
45: // 明 = UD_LIGHT_GREY
46: // □ = UD_GREY
47: // ■ = BGPANEL
48: // = UD_BLACK
49:
50: #include "wxscrollbar.h"
51: #include "wxcolor.h"
52:
53: //#define SCROLL_DEBUG 1
54:
55: #if defined(SCROLL_DEBUG)
56: #define DPRINTF(fmt...) printf(fmt)
57: #else
58: #define DPRINTF(fmt...) /**/
59: #endif
60:
61: #define BORDER_LT (2) // 上(左) 側のフチのピクセル数
62: #define BORDER_RB (1) // 下(右) 側のフチのピクセル数
63: #define THUMB_MIN (8) // つまみの最小サイズ (ピクセル)
64:
65: // 通知イベント。
66: // このコントロールがつまみの位置を変更した場合にイベントが発生する。
67: // SetThumbPosition() などでコードが能動的に変更した場合は発生しない。
68: // イベントの内容は概ね wxScrollEvent 準拠のはず。
69: // ただし、引数は wxScrollEvent だが、イベントタイプは EVT_SCROLL_* では
70: // なく wxCommandEvent の NONO_EVT_SCROLL を使用していること、
71: // wxScrollBar と異なり自動的に親かだれかにイベントが飛んでこないので
72: // 必要な人が適宜 Connect() する必要がある、ところが異なる。
73: wxDEFINE_EVENT(NONO_EVT_SCROLL, wxScrollEvent);
74:
75: // イベントテーブル
76: wxBEGIN_EVENT_TABLE(WXScrollBar, inherited)
77: EVT_SIZE(WXScrollBar::OnSize)
78: EVT_MOUSE_EVENTS(WXScrollBar::OnMouse)
1.1.1.2 ! root 79: EVT_TIMER(wxID_ANY, WXScrollBar::OnTimer)
1.1 root 80: wxEND_EVENT_TABLE()
81:
82: // コンストラクタ
83: //
84: // style は wxSB_VERTICAL か wxSB_HORIZONTAL。
85: // 本家の wxScrollBar は style のデフォルトが wxSB_HORIZONTAL だが、
86: // うちではデフォルト引数にはしない。
87: WXScrollBar::WXScrollBar(wxWindow *parent, wxWindowID id, long style)
1.1.1.2 ! root 88: : inherited(parent, id, wxDefaultPosition, wxSize(16, 16), 0)
1.1 root 89: {
90: if (style == wxSB_VERTICAL) {
91: is_vertical = true;
92: }
1.1.1.2 ! root 93:
! 94: timer.SetOwner(this);
1.1 root 95: }
96:
97: // デストラクタ
98: WXScrollBar::~WXScrollBar()
99: {
100: }
101:
102: // スクロールバーのパラメータを設定する。
103: // 通知イベントは送出しない。
104: //
105: // 各パラメータは wxScrollBar::SetScrollbar() 準拠のはずなのでそちら参照。
106: // SetScrollbar() は wxScrollBar ではなく wxWindow の仮想関数で、ここで定義
107: // すると微妙に型が違うオーバーライドになってしまうため、名前を変えてある。
108: void
109: WXScrollBar::SetScrollParam(int pos_, int thumbsize_, int range_, int pagesize_)
110: {
111: DPRINTF("SetScrollParam: pos=%d thumbsize=%d range=%d pagesize=%d\n",
112: pos_, thumbsize_, range_, pagesize_);
113:
114: thumbsize = thumbsize_;
115: range = range_;
116: pagesize = pagesize_;
117: SetThumbPosition(pos_);
118: }
119:
120: // つまみの位置を設定する。外部インタフェース。
121: // 通知イベントは送出しない。
122: void
123: WXScrollBar::SetThumbPosition(int pos)
124: {
125: thumbpos = pos;
126: DPRINTF("SetThumbPosition: %d\n", pos);
127:
128: // 単位位置の範囲チェック
129: if (thumbpos < 0) {
130: thumbpos = 0;
131: } else if (thumbpos > range - thumbsize) {
132: thumbpos = range - thumbsize;
133: }
134:
135: RefreshThumb();
136: }
137:
138: // つまみの位置を計算して再描画。
139: void
140: WXScrollBar::RefreshThumb()
141: {
142: // パラメータセット前に OnSize されるとここに来る
143: if (range < 1) {
144: return;
145: }
146:
147: // 通常の描画座標は単位値とピクセル数の単純な比で求まる。
148: //
149: // 0 10 90 100 [unit]
150: // +---+-----------------+---+
151: //
152: // 0 20 180 200 [px]
153: // +---+-----------------+---+
154: //
155: // つまみが描画領域に対して小さすぎる場合、ある程度より小さくならないよう
156: // に抑制する (THUMB_MIN [px])。
157: // 例えばここで描画範囲が 50px の場合、つまみの最小サイズは 8px なので
158: // 単位可動域 0-90 を 0-42 ピクセルにマッピングし直して描画する。
159: //
160: // 0 5 8 42 45 50 [px]
161: // +---+-+-------------+-+---+
162:
163: // つまみが小さくなりすぎるのを防ぐ
164: int thumbsize_px = thumbsize * range_px / range;
165: if (thumbsize_px < THUMB_MIN) {
166: thumbsize_px = THUMB_MIN;
167: }
168:
169: // range はスクロールバー領域全体の長さ (図の 100)。
170: // mrange はつまみの大きさを含まない可動域?の長さ (図の 90) とする。
171: // [p1, p2) がつまみのピクセル座標。
172: int mrange = range - thumbsize;
173: if (mrange == 0) {
174: // つまみが全体を占めていると可動域 mrange が 0 になる。
175: p1 = 0;
176: } else {
177: int mrange_px = range_px - thumbsize_px;
178: p1 = thumbpos * mrange_px / mrange;
179: }
180: p2 = p1 + thumbsize_px;
181:
182: // 可動域の上(左)に2ピクセルの枠がある
183: p1 += BORDER_LT;
184: p2 += BORDER_LT;
185:
186: DPRINTF("RefreshThumb: p1=%d p2=%d\n", p1, p2);
187:
188: Refresh();
189: }
190:
191: // つまみの位置を pos に設定し、移動すれば通知イベントを送出する。(内部用)
192: void
193: WXScrollBar::MoveThumb(int pos)
194: {
195: int old = thumbpos;
196:
197: SetThumbPosition(pos);
198:
199: if (thumbpos != old) {
200: // Orientation は wx"SB_"VERTICAL ではなく wxVERTICAL のほう。
201: // (値は同じっぽいからいいけど、どうしてこうなった…)
202: int orient = is_vertical ? wxVERTICAL : wxHORIZONTAL;
203: wxScrollEvent newev(NONO_EVT_SCROLL, GetId(), thumbpos, orient);
204: newev.SetEventObject(this);
205: AddPendingEvent(newev);
206: }
207: }
208:
209: // サイズイベント
210: void
211: WXScrollBar::OnSize(wxSizeEvent& event)
212: {
213: auto s __unused = event.GetSize();
214: DPRINTF("OnSize %d,%d\n", s.x, s.y);
215:
216: // 親クラス
217: inherited::OnSize(event);
218:
219: if (is_vertical) {
220: range_px = bitmap.GetHeight() - BORDER_LT - BORDER_RB;
221: } else {
222: range_px = bitmap.GetWidth() - BORDER_LT - BORDER_RB;
223: }
224:
225: // range_px が変わったので再描画
226: RefreshThumb();
227: }
228:
229: // マウスイベント
230: void
231: WXScrollBar::OnMouse(wxMouseEvent& event)
232: {
233: event.Skip();
234:
1.1.1.2 ! root 235: // 現在位置
! 236: wxPoint pos = event.GetPosition();
! 237: if (is_vertical) {
! 238: mouse_px = pos.y;
! 239: } else {
! 240: mouse_px = pos.x;
! 241: }
! 242: DPRINTF("OnMouse: mouse_px=%d\n", mouse_px);
! 243:
1.1 root 244: int wheel = event.GetWheelRotation();
245: if (wheel != 0) {
246: DPRINTF("OnMouse: wheel=%d\n", wheel);
247:
248: // ホイールは1回で +-120 が飛んでくる。
249: // どこのルールか分からんけどだいたい3行分の移動としているようなので
250: // ここでも踏襲する。
251: MoveThumb(thumbpos - wheel / 40);
252: }
253:
1.1.1.2 ! root 254: if (event.LeftUp()) {
! 255: left_click = false;
! 256: }
! 257:
1.1 root 258: if (event.LeftDown() || event.LeftDClick()) {
1.1.1.2 ! root 259: left_click = true;
! 260:
1.1 root 261: // 左クリック。間隔が速いとダブルクリックになる。
262: // ダブルクリックは LeftDown, LeftUp, LeftDClick, LeftUp の順で来る
263: // ので、LeftDown と LeftDClick を同列で扱っておく。
1.1.1.2 ! root 264: int r = PosCmp();
1.1 root 265: if (r == 0) {
266: // つまみ内左クリックでは何も起きないが、
267: // ドラッグ用に左クリック開始時点の位置を保存。
268: start_thumb = thumbpos;
1.1.1.2 ! root 269: start_px = mouse_px;
! 270: DPRINTF("OnMouse: LeftDown thumbpos=%d start_px=%d\n",
1.1 root 271: start_thumb, start_px);
272: } else {
273: // つまみ外の下地部分ならページアップ、ページダウン
274: DPRINTF("OnMouse: LeftDown cmp=%d\n", r);
275: MoveThumb(thumbpos + r * pagesize);
276: }
1.1.1.2 ! root 277:
! 278: // 押しっぱなし判定のためタイマーを開始。
! 279: timer.Start(500);
1.1 root 280: }
281:
282: if (event.Dragging()) {
283: // ドラッグ開始時からの差分を求めて..
1.1.1.2 ! root 284: int delta = mouse_px - start_px;
1.1 root 285: DPRINTF("OnMouse: Dragging delta=%d\n", delta);
286:
287: // ピクセルでの移動量を変換
288: MoveThumb(start_thumb + delta * range / range_px);
289: }
290: }
291:
1.1.1.2 ! root 292: // タイマーイベント
! 293: void
! 294: WXScrollBar::OnTimer(wxTimerEvent& event)
1.1 root 295: {
1.1.1.2 ! root 296: int r = PosCmp();
! 297: if (left_click && r != 0) {
! 298: // つまみ外で左クリックが続いていたらもう1回動かす
! 299: MoveThumb(thumbpos + r * pagesize);
1.1 root 300:
1.1.1.2 ! root 301: // キーリピートと同じ要領で2回目以降は短くする。
! 302: timer.Start(100);
1.1 root 303: } else {
1.1.1.2 ! root 304: // つまみ内か左クリックが終わっていたら終了
! 305: timer.Stop();
1.1 root 306: }
1.1.1.2 ! root 307: }
1.1 root 308:
1.1.1.2 ! root 309: // mouse_px がつまみ内なら 0 を、つまみより上(左)なら -1、下(右)なら 1 を返す。
! 310: int
! 311: WXScrollBar::PosCmp() const
! 312: {
! 313: if (mouse_px < p1) {
1.1 root 314: return -1;
315: }
1.1.1.2 ! root 316: if (mouse_px > p2) {
1.1 root 317: return 1;
318: }
319: return 0;
320: }
321:
322: // 描画
323: void
324: WXScrollBar::Draw()
325: {
326: const int& w = bitmap.GetWidth();
327: const int& h = bitmap.GetHeight();
328:
329: if (is_vertical) {
330: // 背景
331: bitmap.DrawLineH(UD_LIGHT_GREY, 0, 0, w);
332: bitmap.DrawLineH(UD_LIGHT_GREY, 0, h - 1, w);
333: bitmap.DrawLineV(UD_LIGHT_GREY, w - 1, 1, h - 1);
334: bitmap.FillRect(UD_GREY, 0, 1, w - 1, h - 2);
335:
336: // つまみ
337: bitmap.FillRect(BGPANEL, 1, p1, w - 4, p2 - p1 - 1);
338: bitmap.DrawLineH(UD_BLACK, 1, p2 - 1, w - 1);
339: bitmap.DrawLineV(UD_BLACK, w - 2, p1, p2);
340: } else {
341: // 背景
342: bitmap.DrawLineV(UD_LIGHT_GREY, 0, 0, h);
343: bitmap.DrawLineV(UD_LIGHT_GREY, w - 1, 0, h);
344: bitmap.DrawLineH(UD_LIGHT_GREY, 1, h - 1, w - 1);
345: bitmap.FillRect(UD_GREY, 1, 0, w - 2, h - 1);
346:
347: // つまみ
348: bitmap.FillRect(BGPANEL, p1, 1, p2 - p1 - 1, h - 4);
349: bitmap.DrawLineV(UD_BLACK, p2 - 1, 1, h - 1);
350: bitmap.DrawLineH(UD_BLACK, p1, h - 2, p2);
351: }
352: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.