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

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: }

unix.superglobalmegacorp.com

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