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

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

unix.superglobalmegacorp.com

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