--- nono/wx/wxscrollbar.cpp 2026/04/29 17:05:16 1.1.1.2 +++ nono/wx/wxscrollbar.cpp 2026/04/29 17:05:44 1.1.1.4 @@ -60,7 +60,6 @@ #define BORDER_LT (2) // 上(左) 側のフチのピクセル数 #define BORDER_RB (1) // 下(右) 側のフチのピクセル数 -#define THUMB_MIN (8) // つまみの最小サイズ (ピクセル) // 通知イベント。 // このコントロールがつまみの位置を変更した場合にイベントが発生する。 @@ -85,12 +84,16 @@ wxEND_EVENT_TABLE() // 本家の wxScrollBar は style のデフォルトが wxSB_HORIZONTAL だが、 // うちではデフォルト引数にはしない。 WXScrollBar::WXScrollBar(wxWindow *parent, wxWindowID id, long style) - : inherited(parent, id, wxDefaultPosition, wxSize(16, 16), 0) + : inherited(parent, id) { + SetName("WXScrollBar"); + if (style == wxSB_VERTICAL) { is_vertical = true; } + FontChanged(); + timer.SetOwner(this); } @@ -99,6 +102,58 @@ WXScrollBar::~WXScrollBar() { } +// フォントサイズ変更 +void +WXScrollBar::FontChanged() +{ + inherited::FontChanged(); + DPRINTF("%s %d\n", __method__, font_height); + + switch (font_height) { + case 12: + subpx = 16; + break; + case 16: + subpx = 20; + break; + case 24: + subpx = 32; + break; + default: + PANIC("Unexpected font_height=%d", font_height); + } + + // つまみの最小サイズ。適当に幅の半分。 + thumbmin = subpx / 2; + + Fit(); +} + +void +WXScrollBar::Fit() +{ + // 最小サイズを設定。 + wxSize minsize; + if (is_vertical) { + minsize.x = subpx; + minsize.y = font_height; + } else { + minsize.x = font_width * 2; + minsize.y = subpx; + } + DPRINTF("%s GetSize=(%d,%d) Best=(%d,%d)\n", __method__, + GetSize().x, GetSize().y, minsize.x, minsize.y); + SetSizeHints(minsize, wxDefaultSize); + + // 主方向は最小サイズを下回らなければ維持、 + // 副方向は常に規定サイズ。 + if (is_vertical) { + SetSize(subpx, GetSize().y); + } else { + SetSize(GetSize().x, subpx); + } +} + // スクロールバーのパラメータを設定する。 // 通知イベントは送出しない。 // @@ -119,29 +174,20 @@ WXScrollBar::SetScrollParam(int pos_, in // つまみの位置を設定する。外部インタフェース。 // 通知イベントは送出しない。 -void +// つまみが移動すれば true を、移動しなければ false を返す。 +bool WXScrollBar::SetThumbPosition(int pos) { - thumbpos = pos; - DPRINTF("SetThumbPosition: %d\n", pos); - - // 単位位置の範囲チェック - if (thumbpos < 0) { - thumbpos = 0; - } else if (thumbpos > range - thumbsize) { - thumbpos = range - thumbsize; + // パラメータセット前にリサイズされるとここに来る + if (range < 1) { + return false; } - RefreshThumb(); -} - -// つまみの位置を計算して再描画。 -void -WXScrollBar::RefreshThumb() -{ - // パラメータセット前に OnSize されるとここに来る - if (range < 1) { - return; + // 単位位置の範囲チェック + if (pos < 0) { + pos = 0; + } else if (pos > range - thumbsize) { + pos = range - thumbsize; } // 通常の描画座標は単位値とピクセル数の単純な比で求まる。 @@ -153,7 +199,7 @@ WXScrollBar::RefreshThumb() // +---+-----------------+---+ // // つまみが描画領域に対して小さすぎる場合、ある程度より小さくならないよう - // に抑制する (THUMB_MIN [px])。 + // に抑制する (thumbmin [px])。 // 例えばここで描画範囲が 50px の場合、つまみの最小サイズは 8px なので // 単位可動域 0-90 を 0-42 ピクセルにマッピングし直して描画する。 // @@ -162,20 +208,22 @@ WXScrollBar::RefreshThumb() // つまみが小さくなりすぎるのを防ぐ int thumbsize_px = thumbsize * range_px / range; - if (thumbsize_px < THUMB_MIN) { - thumbsize_px = THUMB_MIN; + if (thumbsize_px < thumbmin) { + thumbsize_px = thumbmin; } // range はスクロールバー領域全体の長さ (図の 100)。 // mrange はつまみの大きさを含まない可動域?の長さ (図の 90) とする。 // [p1, p2) がつまみのピクセル座標。 + int oldp1 = p1; + int oldp2 = p2; int mrange = range - thumbsize; if (mrange == 0) { // つまみが全体を占めていると可動域 mrange が 0 になる。 p1 = 0; } else { int mrange_px = range_px - thumbsize_px; - p1 = thumbpos * mrange_px / mrange; + p1 = pos * mrange_px / mrange; } p2 = p1 + thumbsize_px; @@ -183,20 +231,26 @@ WXScrollBar::RefreshThumb() p1 += BORDER_LT; p2 += BORDER_LT; - DPRINTF("RefreshThumb: p1=%d p2=%d\n", p1, p2); + // 移動していない (表示位置が変わっていない) なら false で帰る。 + // 目一杯を占めていて移動できないつまみをドラッグしてマウス移動 + // しても、つまみが移動しないのだから、スクロールも起こさないため。 + DPRINTF("SetThumbPosition: oldpos=%d newpos=%d oldp=%d-%d p=%d-%d\n", + thumbpos, pos, oldp1, oldp2, p1, p2); + if (p1 == oldp1 && p2 == oldp2) { + return false; + } + thumbpos = pos; + DPRINTF("SetThumbPosition: new pos=%d\n", thumbpos); Refresh(); + return true; } // つまみの位置を pos に設定し、移動すれば通知イベントを送出する。(内部用) void WXScrollBar::MoveThumb(int pos) { - int old = thumbpos; - - SetThumbPosition(pos); - - if (thumbpos != old) { + if (SetThumbPosition(pos)) { // Orientation は wx"SB_"VERTICAL ではなく wxVERTICAL のほう。 // (値は同じっぽいからいいけど、どうしてこうなった…) int orient = is_vertical ? wxVERTICAL : wxHORIZONTAL; @@ -206,24 +260,22 @@ WXScrollBar::MoveThumb(int pos) } } -// サイズイベント void WXScrollBar::OnSize(wxSizeEvent& event) { - auto s __unused = event.GetSize(); - DPRINTF("OnSize %d,%d\n", s.x, s.y); - - // 親クラス - inherited::OnSize(event); + const wxSize& size = GetSize(); + DPRINTF("%s size=(%d,%d)\n", __method__, size.x, size.y); + // スクロールバーの論理パラメータは変わらず、描画情報が変わる。 if (is_vertical) { - range_px = bitmap.GetHeight() - BORDER_LT - BORDER_RB; + range_px = size.y - BORDER_LT - BORDER_RB; } else { - range_px = bitmap.GetWidth() - BORDER_LT - BORDER_RB; + range_px = size.x - BORDER_LT - BORDER_RB; } - // range_px が変わったので再描画 - RefreshThumb(); + SetThumbPosition(thumbpos); + + event.Skip(); } // マウスイベント @@ -323,8 +375,16 @@ WXScrollBar::PosCmp() const void WXScrollBar::Draw() { - const int& w = bitmap.GetWidth(); - const int& h = bitmap.GetHeight(); + // パラメータセット前に表示してしまうとここに来る。 + if (__predict_false(range < 1)) { + return; + } + + const int w = bitmap.GetWidth(); + const int h = bitmap.GetHeight(); + if (__predict_false(w < 4 || h < 4)) { + return; + } if (is_vertical) { // 背景 @@ -334,7 +394,7 @@ WXScrollBar::Draw() bitmap.FillRect(UD_GREY, 0, 1, w - 1, h - 2); // つまみ - bitmap.FillRect(BGPANEL, 1, p1, w - 4, p2 - p1 - 1); + bitmap.FillRect(BGPANEL, 1, p1, w - 3, p2 - p1 - 1); bitmap.DrawLineH(UD_BLACK, 1, p2 - 1, w - 1); bitmap.DrawLineV(UD_BLACK, w - 2, p1, p2); } else { @@ -345,7 +405,7 @@ WXScrollBar::Draw() bitmap.FillRect(UD_GREY, 1, 0, w - 2, h - 1); // つまみ - bitmap.FillRect(BGPANEL, p1, 1, p2 - p1 - 1, h - 4); + bitmap.FillRect(BGPANEL, p1, 1, p2 - p1 - 1, h - 3); bitmap.DrawLineV(UD_BLACK, p2 - 1, 1, h - 1); bitmap.DrawLineH(UD_BLACK, p1, h - 2, p2); }