|
|
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) ! 79: wxEND_EVENT_TABLE() ! 80: ! 81: // コンストラクタ ! 82: // ! 83: // style は wxSB_VERTICAL か wxSB_HORIZONTAL。 ! 84: // 本家の wxScrollBar は style のデフォルトが wxSB_HORIZONTAL だが、 ! 85: // うちではデフォルト引数にはしない。 ! 86: WXScrollBar::WXScrollBar(wxWindow *parent, wxWindowID id, long style) ! 87: : inherited(parent, id, wxSize(16, 16), 0) ! 88: { ! 89: if (style == wxSB_VERTICAL) { ! 90: is_vertical = true; ! 91: } ! 92: } ! 93: ! 94: // デストラクタ ! 95: WXScrollBar::~WXScrollBar() ! 96: { ! 97: } ! 98: ! 99: // スクロールバーのパラメータを設定する。 ! 100: // 通知イベントは送出しない。 ! 101: // ! 102: // 各パラメータは wxScrollBar::SetScrollbar() 準拠のはずなのでそちら参照。 ! 103: // SetScrollbar() は wxScrollBar ではなく wxWindow の仮想関数で、ここで定義 ! 104: // すると微妙に型が違うオーバーライドになってしまうため、名前を変えてある。 ! 105: void ! 106: WXScrollBar::SetScrollParam(int pos_, int thumbsize_, int range_, int pagesize_) ! 107: { ! 108: DPRINTF("SetScrollParam: pos=%d thumbsize=%d range=%d pagesize=%d\n", ! 109: pos_, thumbsize_, range_, pagesize_); ! 110: ! 111: thumbsize = thumbsize_; ! 112: range = range_; ! 113: pagesize = pagesize_; ! 114: SetThumbPosition(pos_); ! 115: } ! 116: ! 117: // つまみの位置を設定する。外部インタフェース。 ! 118: // 通知イベントは送出しない。 ! 119: void ! 120: WXScrollBar::SetThumbPosition(int pos) ! 121: { ! 122: thumbpos = pos; ! 123: DPRINTF("SetThumbPosition: %d\n", pos); ! 124: ! 125: // 単位位置の範囲チェック ! 126: if (thumbpos < 0) { ! 127: thumbpos = 0; ! 128: } else if (thumbpos > range - thumbsize) { ! 129: thumbpos = range - thumbsize; ! 130: } ! 131: ! 132: RefreshThumb(); ! 133: } ! 134: ! 135: // つまみの位置を計算して再描画。 ! 136: void ! 137: WXScrollBar::RefreshThumb() ! 138: { ! 139: // パラメータセット前に OnSize されるとここに来る ! 140: if (range < 1) { ! 141: return; ! 142: } ! 143: ! 144: // 通常の描画座標は単位値とピクセル数の単純な比で求まる。 ! 145: // ! 146: // 0 10 90 100 [unit] ! 147: // +---+-----------------+---+ ! 148: // ! 149: // 0 20 180 200 [px] ! 150: // +---+-----------------+---+ ! 151: // ! 152: // つまみが描画領域に対して小さすぎる場合、ある程度より小さくならないよう ! 153: // に抑制する (THUMB_MIN [px])。 ! 154: // 例えばここで描画範囲が 50px の場合、つまみの最小サイズは 8px なので ! 155: // 単位可動域 0-90 を 0-42 ピクセルにマッピングし直して描画する。 ! 156: // ! 157: // 0 5 8 42 45 50 [px] ! 158: // +---+-+-------------+-+---+ ! 159: ! 160: // つまみが小さくなりすぎるのを防ぐ ! 161: int thumbsize_px = thumbsize * range_px / range; ! 162: if (thumbsize_px < THUMB_MIN) { ! 163: thumbsize_px = THUMB_MIN; ! 164: } ! 165: ! 166: // range はスクロールバー領域全体の長さ (図の 100)。 ! 167: // mrange はつまみの大きさを含まない可動域?の長さ (図の 90) とする。 ! 168: // [p1, p2) がつまみのピクセル座標。 ! 169: int mrange = range - thumbsize; ! 170: if (mrange == 0) { ! 171: // つまみが全体を占めていると可動域 mrange が 0 になる。 ! 172: p1 = 0; ! 173: } else { ! 174: int mrange_px = range_px - thumbsize_px; ! 175: p1 = thumbpos * mrange_px / mrange; ! 176: } ! 177: p2 = p1 + thumbsize_px; ! 178: ! 179: // 可動域の上(左)に2ピクセルの枠がある ! 180: p1 += BORDER_LT; ! 181: p2 += BORDER_LT; ! 182: ! 183: DPRINTF("RefreshThumb: p1=%d p2=%d\n", p1, p2); ! 184: ! 185: Refresh(); ! 186: } ! 187: ! 188: // つまみの位置を pos に設定し、移動すれば通知イベントを送出する。(内部用) ! 189: void ! 190: WXScrollBar::MoveThumb(int pos) ! 191: { ! 192: int old = thumbpos; ! 193: ! 194: SetThumbPosition(pos); ! 195: ! 196: if (thumbpos != old) { ! 197: // Orientation は wx"SB_"VERTICAL ではなく wxVERTICAL のほう。 ! 198: // (値は同じっぽいからいいけど、どうしてこうなった…) ! 199: int orient = is_vertical ? wxVERTICAL : wxHORIZONTAL; ! 200: wxScrollEvent newev(NONO_EVT_SCROLL, GetId(), thumbpos, orient); ! 201: newev.SetEventObject(this); ! 202: AddPendingEvent(newev); ! 203: } ! 204: } ! 205: ! 206: // サイズイベント ! 207: void ! 208: WXScrollBar::OnSize(wxSizeEvent& event) ! 209: { ! 210: auto s __unused = event.GetSize(); ! 211: DPRINTF("OnSize %d,%d\n", s.x, s.y); ! 212: ! 213: // 親クラス ! 214: inherited::OnSize(event); ! 215: ! 216: if (is_vertical) { ! 217: range_px = bitmap.GetHeight() - BORDER_LT - BORDER_RB; ! 218: } else { ! 219: range_px = bitmap.GetWidth() - BORDER_LT - BORDER_RB; ! 220: } ! 221: ! 222: // range_px が変わったので再描画 ! 223: RefreshThumb(); ! 224: } ! 225: ! 226: // マウスイベント ! 227: void ! 228: WXScrollBar::OnMouse(wxMouseEvent& event) ! 229: { ! 230: wxPoint pos = event.GetPosition(); ! 231: ! 232: event.Skip(); ! 233: ! 234: int wheel = event.GetWheelRotation(); ! 235: if (wheel != 0) { ! 236: DPRINTF("OnMouse: wheel=%d\n", wheel); ! 237: ! 238: // ホイールは1回で +-120 が飛んでくる。 ! 239: // どこのルールか分からんけどだいたい3行分の移動としているようなので ! 240: // ここでも踏襲する。 ! 241: MoveThumb(thumbpos - wheel / 40); ! 242: } ! 243: ! 244: if (event.LeftDown() || event.LeftDClick()) { ! 245: // 左クリック。間隔が速いとダブルクリックになる。 ! 246: // ダブルクリックは LeftDown, LeftUp, LeftDClick, LeftUp の順で来る ! 247: // ので、LeftDown と LeftDClick を同列で扱っておく。 ! 248: int r = PosCmp(pos); ! 249: if (r == 0) { ! 250: // つまみ内左クリックでは何も起きないが、 ! 251: // ドラッグ用に左クリック開始時点の位置を保存。 ! 252: start_thumb = thumbpos; ! 253: if (is_vertical) { ! 254: start_px = pos.y; ! 255: } else { ! 256: start_px = pos.x; ! 257: } ! 258: DPRINTF("OnMouse: LeftDown start_pos=%d px=%d\n", ! 259: start_thumb, start_px); ! 260: } else { ! 261: // つまみ外の下地部分ならページアップ、ページダウン ! 262: DPRINTF("OnMouse: LeftDown cmp=%d\n", r); ! 263: MoveThumb(thumbpos + r * pagesize); ! 264: } ! 265: } ! 266: ! 267: if (event.Dragging()) { ! 268: // ドラッグ開始時からの差分を求めて.. ! 269: int delta; ! 270: if (is_vertical) { ! 271: delta = pos.y - start_px; ! 272: } else { ! 273: delta = pos.x - start_px; ! 274: } ! 275: DPRINTF("OnMouse: Dragging delta=%d\n", delta); ! 276: ! 277: // ピクセルでの移動量を変換 ! 278: MoveThumb(start_thumb + delta * range / range_px); ! 279: } ! 280: } ! 281: ! 282: // pt がつまみ内なら 0 を、つまみより上(左)なら -1、下(右)なら 1 を返す。 ! 283: int ! 284: WXScrollBar::PosCmp(const wxPoint& pt) const ! 285: { ! 286: int p; ! 287: ! 288: if (is_vertical) { ! 289: p = pt.y; ! 290: } else { ! 291: p = pt.x; ! 292: } ! 293: ! 294: if (p < p1) { ! 295: return -1; ! 296: } ! 297: if (p >= p2) { ! 298: return 1; ! 299: } ! 300: return 0; ! 301: } ! 302: ! 303: // 描画 ! 304: void ! 305: WXScrollBar::Draw() ! 306: { ! 307: const int& w = bitmap.GetWidth(); ! 308: const int& h = bitmap.GetHeight(); ! 309: ! 310: if (is_vertical) { ! 311: // 背景 ! 312: bitmap.DrawLineH(UD_LIGHT_GREY, 0, 0, w); ! 313: bitmap.DrawLineH(UD_LIGHT_GREY, 0, h - 1, w); ! 314: bitmap.DrawLineV(UD_LIGHT_GREY, w - 1, 1, h - 1); ! 315: bitmap.FillRect(UD_GREY, 0, 1, w - 1, h - 2); ! 316: ! 317: // つまみ ! 318: bitmap.FillRect(BGPANEL, 1, p1, w - 4, p2 - p1 - 1); ! 319: bitmap.DrawLineH(UD_BLACK, 1, p2 - 1, w - 1); ! 320: bitmap.DrawLineV(UD_BLACK, w - 2, p1, p2); ! 321: } else { ! 322: // 背景 ! 323: bitmap.DrawLineV(UD_LIGHT_GREY, 0, 0, h); ! 324: bitmap.DrawLineV(UD_LIGHT_GREY, w - 1, 0, h); ! 325: bitmap.DrawLineH(UD_LIGHT_GREY, 1, h - 1, w - 1); ! 326: bitmap.FillRect(UD_GREY, 1, 0, w - 2, h - 1); ! 327: ! 328: // つまみ ! 329: bitmap.FillRect(BGPANEL, p1, 1, p2 - p1 - 1, h - 4); ! 330: bitmap.DrawLineV(UD_BLACK, p2 - 1, 1, h - 1); ! 331: bitmap.DrawLineH(UD_BLACK, p1, h - 2, p2); ! 332: } ! 333: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.