Annotation of nono/wx/wxscrollbar.cpp, revision 1.1.1.3

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: 
                     64: // 通知イベント。
                     65: // このコントロールがつまみの位置を変更した場合にイベントが発生する。
                     66: // SetThumbPosition() などでコードが能動的に変更した場合は発生しない。
                     67: // イベントの内容は概ね wxScrollEvent 準拠のはず。
                     68: // ただし、引数は wxScrollEvent だが、イベントタイプは EVT_SCROLL_* では
                     69: // なく wxCommandEvent の NONO_EVT_SCROLL を使用していること、
                     70: // wxScrollBar と異なり自動的に親かだれかにイベントが飛んでこないので
                     71: // 必要な人が適宜 Connect() する必要がある、ところが異なる。
                     72: wxDEFINE_EVENT(NONO_EVT_SCROLL, wxScrollEvent);
                     73: 
                     74: // イベントテーブル
                     75: wxBEGIN_EVENT_TABLE(WXScrollBar, inherited)
                     76:        EVT_SIZE(WXScrollBar::OnSize)
                     77:        EVT_MOUSE_EVENTS(WXScrollBar::OnMouse)
1.1.1.2   root       78:        EVT_TIMER(wxID_ANY, WXScrollBar::OnTimer)
1.1       root       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)
1.1.1.2   root       87:        : inherited(parent, id, wxDefaultPosition, wxSize(16, 16), 0)
1.1       root       88: {
                     89:        if (style == wxSB_VERTICAL) {
                     90:                is_vertical = true;
                     91:        }
1.1.1.2   root       92: 
1.1.1.3 ! root       93:        FontChanged();
        !            94: 
1.1.1.2   root       95:        timer.SetOwner(this);
1.1       root       96: }
                     97: 
                     98: // デストラクタ
                     99: WXScrollBar::~WXScrollBar()
                    100: {
                    101: }
                    102: 
1.1.1.3 ! root      103: // フォントサイズ変更
        !           104: void
        !           105: WXScrollBar::FontChanged()
        !           106: {
        !           107:        inherited::FontChanged();
        !           108: 
        !           109:        int newpx;
        !           110:        switch (font_height) {
        !           111:         case 12:
        !           112:                newpx = 16;
        !           113:                break;
        !           114:         case 16:
        !           115:                newpx = 20;
        !           116:                break;
        !           117:         case 24:
        !           118:                newpx = 32;
        !           119:                break;
        !           120:         default:
        !           121:                PANIC("Unexpected font_height=%d", font_height);
        !           122:        }
        !           123: 
        !           124:        // つまみの最小サイズ。適当に幅の半分。
        !           125:        thumbmin = newpx / 2;
        !           126: 
        !           127:        // コントロールの大きさを変更
        !           128:        wxSize size = GetClientSize();
        !           129:        if (is_vertical) {
        !           130:                size.x = newpx;
        !           131:        } else {
        !           132:                size.y = newpx;
        !           133:        }
        !           134:        SetClientSize(size);
        !           135:        SetMinClientSize(size);
        !           136: }
        !           137: 
1.1       root      138: // スクロールバーのパラメータを設定する。
                    139: // 通知イベントは送出しない。
                    140: //
                    141: // 各パラメータは wxScrollBar::SetScrollbar() 準拠のはずなのでそちら参照。
                    142: // SetScrollbar() は wxScrollBar ではなく wxWindow の仮想関数で、ここで定義
                    143: // すると微妙に型が違うオーバーライドになってしまうため、名前を変えてある。
                    144: void
                    145: WXScrollBar::SetScrollParam(int pos_, int thumbsize_, int range_, int pagesize_)
                    146: {
                    147:        DPRINTF("SetScrollParam: pos=%d thumbsize=%d range=%d pagesize=%d\n",
                    148:                pos_, thumbsize_, range_, pagesize_);
                    149: 
                    150:        thumbsize = thumbsize_;
                    151:        range = range_;
                    152:        pagesize = pagesize_;
                    153:        SetThumbPosition(pos_);
                    154: }
                    155: 
                    156: // つまみの位置を設定する。外部インタフェース。
                    157: // 通知イベントは送出しない。
1.1.1.3 ! root      158: // つまみが移動すれば true を、移動しなければ false を返す。
        !           159: bool
1.1       root      160: WXScrollBar::SetThumbPosition(int pos)
                    161: {
                    162:        // パラメータセット前に OnSize されるとここに来る
                    163:        if (range < 1) {
1.1.1.3 ! root      164:                return false;
        !           165:        }
        !           166: 
        !           167:        // 単位位置の範囲チェック
        !           168:        if (pos < 0) {
        !           169:                pos = 0;
        !           170:        } else if (pos > range - thumbsize) {
        !           171:                pos = range - thumbsize;
1.1       root      172:        }
                    173: 
                    174:        // 通常の描画座標は単位値とピクセル数の単純な比で求まる。
                    175:        //
                    176:        //  0    10                90  100 [unit]
                    177:        //  +---+-----------------+---+
                    178:        //
                    179:        //  0    20                180 200 [px]
                    180:        //  +---+-----------------+---+
                    181:        //
                    182:        // つまみが描画領域に対して小さすぎる場合、ある程度より小さくならないよう
1.1.1.3 ! root      183:        // に抑制する (thumbmin [px])。
1.1       root      184:        // 例えばここで描画範囲が 50px の場合、つまみの最小サイズは 8px なので
                    185:        // 単位可動域 0-90 を 0-42 ピクセルにマッピングし直して描画する。
                    186:        //
                    187:        //  0    5 8            42 45  50 [px]
                    188:        //  +---+-+-------------+-+---+
                    189: 
                    190:        // つまみが小さくなりすぎるのを防ぐ
                    191:        int thumbsize_px = thumbsize * range_px / range;
1.1.1.3 ! root      192:        if (thumbsize_px < thumbmin) {
        !           193:                thumbsize_px = thumbmin;
1.1       root      194:        }
                    195: 
                    196:        // range はスクロールバー領域全体の長さ (図の 100)。
                    197:        // mrange はつまみの大きさを含まない可動域?の長さ (図の 90) とする。
                    198:        // [p1, p2) がつまみのピクセル座標。
1.1.1.3 ! root      199:        int oldp1 = p1;
        !           200:        int oldp2 = p2;
1.1       root      201:        int mrange = range - thumbsize;
                    202:        if (mrange == 0) {
                    203:                // つまみが全体を占めていると可動域 mrange が 0 になる。
                    204:                p1 = 0;
                    205:        } else {
                    206:                int mrange_px = range_px - thumbsize_px;
1.1.1.3 ! root      207:                p1 = pos * mrange_px / mrange;
1.1       root      208:        }
                    209:        p2 = p1 + thumbsize_px;
                    210: 
                    211:        // 可動域の上(左)に2ピクセルの枠がある
                    212:        p1 += BORDER_LT;
                    213:        p2 += BORDER_LT;
                    214: 
1.1.1.3 ! root      215:        // 移動していない (表示位置が変わっていない) なら false で帰る。
        !           216:        // 目一杯を占めていて移動できないつまみをドラッグしてマウス移動
        !           217:        // しても、つまみが移動しないのだから、スクロールも起こさないため。
        !           218:        DPRINTF("SetThumbPosition: oldpos=%d newpos=%d oldp=%d-%d p=%d-%d\n",
        !           219:                thumbpos, pos, oldp1, oldp2, p1, p2);
        !           220:        if (p1 == oldp1 && p2 == oldp2) {
        !           221:                return false;
        !           222:        }
1.1       root      223: 
1.1.1.3 ! root      224:        thumbpos = pos;
        !           225:        DPRINTF("SetThumbPosition: new pos=%d\n", thumbpos);
1.1       root      226:        Refresh();
1.1.1.3 ! root      227:        return true;
1.1       root      228: }
                    229: 
                    230: // つまみの位置を pos に設定し、移動すれば通知イベントを送出する。(内部用)
                    231: void
                    232: WXScrollBar::MoveThumb(int pos)
                    233: {
1.1.1.3 ! root      234:        if (SetThumbPosition(pos)) {
1.1       root      235:                // Orientation は wx"SB_"VERTICAL ではなく wxVERTICAL のほう。
                    236:                // (値は同じっぽいからいいけど、どうしてこうなった…)
                    237:                int orient = is_vertical ? wxVERTICAL : wxHORIZONTAL;
                    238:                wxScrollEvent newev(NONO_EVT_SCROLL, GetId(), thumbpos, orient);
                    239:                newev.SetEventObject(this);
                    240:                AddPendingEvent(newev);
                    241:        }
                    242: }
                    243: 
1.1.1.3 ! root      244: // コントロールの主方向(スクロール方向)の最小ピクセル数を返す
        !           245: int
        !           246: WXScrollBar::GetMinLength() const
        !           247: {
        !           248:        return thumbmin + BORDER_LT + BORDER_RB;
        !           249: }
        !           250: 
1.1       root      251: // サイズイベント
                    252: void
                    253: WXScrollBar::OnSize(wxSizeEvent& event)
                    254: {
                    255:        auto s __unused = event.GetSize();
                    256:        DPRINTF("OnSize %d,%d\n", s.x, s.y);
                    257: 
                    258:        // 親クラス
                    259:        inherited::OnSize(event);
                    260: 
                    261:        if (is_vertical) {
                    262:                range_px = bitmap.GetHeight() - BORDER_LT - BORDER_RB;
                    263:        } else {
                    264:                range_px = bitmap.GetWidth() - BORDER_LT - BORDER_RB;
                    265:        }
                    266: 
                    267:        // range_px が変わったので再描画
1.1.1.3 ! root      268:        SetThumbPosition(thumbpos);
1.1       root      269: }
                    270: 
                    271: // マウスイベント
                    272: void
                    273: WXScrollBar::OnMouse(wxMouseEvent& event)
                    274: {
                    275:        event.Skip();
                    276: 
1.1.1.2   root      277:        // 現在位置
                    278:        wxPoint pos = event.GetPosition();
                    279:        if (is_vertical) {
                    280:                mouse_px = pos.y;
                    281:        } else {
                    282:                mouse_px = pos.x;
                    283:        }
                    284:        DPRINTF("OnMouse: mouse_px=%d\n", mouse_px);
                    285: 
1.1       root      286:        int wheel = event.GetWheelRotation();
                    287:        if (wheel != 0) {
                    288:                DPRINTF("OnMouse: wheel=%d\n", wheel);
                    289: 
                    290:                // ホイールは1回で +-120 が飛んでくる。
                    291:                // どこのルールか分からんけどだいたい3行分の移動としているようなので
                    292:                // ここでも踏襲する。
                    293:                MoveThumb(thumbpos - wheel / 40);
                    294:        }
                    295: 
1.1.1.2   root      296:        if (event.LeftUp()) {
                    297:                left_click = false;
                    298:        }
                    299: 
1.1       root      300:        if (event.LeftDown() || event.LeftDClick()) {
1.1.1.2   root      301:                left_click = true;
                    302: 
1.1       root      303:                // 左クリック。間隔が速いとダブルクリックになる。
                    304:                // ダブルクリックは LeftDown, LeftUp, LeftDClick, LeftUp の順で来る
                    305:                // ので、LeftDown と LeftDClick を同列で扱っておく。
1.1.1.2   root      306:                int r = PosCmp();
1.1       root      307:                if (r == 0) {
                    308:                        // つまみ内左クリックでは何も起きないが、
                    309:                        // ドラッグ用に左クリック開始時点の位置を保存。
                    310:                        start_thumb = thumbpos;
1.1.1.2   root      311:                        start_px = mouse_px;
                    312:                        DPRINTF("OnMouse: LeftDown thumbpos=%d start_px=%d\n",
1.1       root      313:                                start_thumb, start_px);
                    314:                } else {
                    315:                        // つまみ外の下地部分ならページアップ、ページダウン
                    316:                        DPRINTF("OnMouse: LeftDown cmp=%d\n", r);
                    317:                        MoveThumb(thumbpos + r * pagesize);
                    318:                }
1.1.1.2   root      319: 
                    320:                // 押しっぱなし判定のためタイマーを開始。
                    321:                timer.Start(500);
1.1       root      322:        }
                    323: 
                    324:        if (event.Dragging()) {
                    325:                // ドラッグ開始時からの差分を求めて..
1.1.1.2   root      326:                int delta = mouse_px - start_px;
1.1       root      327:                DPRINTF("OnMouse: Dragging delta=%d\n", delta);
                    328: 
                    329:                // ピクセルでの移動量を変換
                    330:                MoveThumb(start_thumb + delta * range / range_px);
                    331:        }
                    332: }
                    333: 
1.1.1.2   root      334: // タイマーイベント
                    335: void
                    336: WXScrollBar::OnTimer(wxTimerEvent& event)
1.1       root      337: {
1.1.1.2   root      338:        int r = PosCmp();
                    339:        if (left_click && r != 0) {
                    340:                // つまみ外で左クリックが続いていたらもう1回動かす
                    341:                MoveThumb(thumbpos + r * pagesize);
1.1       root      342: 
1.1.1.2   root      343:                // キーリピートと同じ要領で2回目以降は短くする。
                    344:                timer.Start(100);
1.1       root      345:        } else {
1.1.1.2   root      346:                // つまみ内か左クリックが終わっていたら終了
                    347:                timer.Stop();
1.1       root      348:        }
1.1.1.2   root      349: }
1.1       root      350: 
1.1.1.2   root      351: // mouse_px がつまみ内なら 0 を、つまみより上(左)なら -1、下(右)なら 1 を返す。
                    352: int
                    353: WXScrollBar::PosCmp() const
                    354: {
                    355:        if (mouse_px < p1) {
1.1       root      356:                return -1;
                    357:        }
1.1.1.2   root      358:        if (mouse_px > p2) {
1.1       root      359:                return 1;
                    360:        }
                    361:        return 0;
                    362: }
                    363: 
                    364: // 描画
                    365: void
                    366: WXScrollBar::Draw()
                    367: {
                    368:        const int& w = bitmap.GetWidth();
                    369:        const int& h = bitmap.GetHeight();
                    370: 
                    371:        if (is_vertical) {
                    372:                // 背景
                    373:                bitmap.DrawLineH(UD_LIGHT_GREY, 0, 0, w);
                    374:                bitmap.DrawLineH(UD_LIGHT_GREY, 0, h - 1, w);
                    375:                bitmap.DrawLineV(UD_LIGHT_GREY, w - 1, 1, h - 1);
                    376:                bitmap.FillRect(UD_GREY, 0, 1, w - 1, h - 2);
                    377: 
                    378:                // つまみ
                    379:                bitmap.FillRect(BGPANEL, 1, p1, w - 4, p2 - p1 - 1);
                    380:                bitmap.DrawLineH(UD_BLACK, 1, p2 - 1, w - 1);
                    381:                bitmap.DrawLineV(UD_BLACK, w - 2, p1, p2);
                    382:        } else {
                    383:                // 背景
                    384:                bitmap.DrawLineV(UD_LIGHT_GREY, 0, 0, h);
                    385:                bitmap.DrawLineV(UD_LIGHT_GREY, w - 1, 0, h);
                    386:                bitmap.DrawLineH(UD_LIGHT_GREY, 1, h - 1, w - 1);
                    387:                bitmap.FillRect(UD_GREY, 1, 0, w - 2, h - 1);
                    388: 
                    389:                // つまみ
                    390:                bitmap.FillRect(BGPANEL, p1, 1, p2 - p1 - 1, h - 4);
                    391:                bitmap.DrawLineV(UD_BLACK, p2 - 1, 1, h - 1);
                    392:                bitmap.DrawLineH(UD_BLACK, p1, h - 2, p2);
                    393:        }
                    394: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.