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

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

unix.superglobalmegacorp.com

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