#include #include "scbkgwnd.h" #include #include #include #define SCROLLBAR_SEP 4 #define TIMER_SMOOTHSCROLLY 8873 #define TIMER_SMOOTHSCROLLX 8874 #define SMOOTH_STEPS 5 #define DEFAULT_BGCOLOR RGB(0,0,0) ScrlBkgWnd::ScrlBkgWnd() { inDestroy = FALSE; bmp = NULL; bgColor = DEFAULT_BGCOLOR; scrollX = 0; scrollY = 0; dbbuffer = 1; needSetSliders = FALSE; lineHeight = 16; wantsep = 0; wantTileBg = true; lastratio = 1.0; MEMSET(&smsqr, 0, sizeof(RECT)); in_set_slider_position = 0; smoothScrollYInc = smoothScrollXInc = 0; smoothScrollYCur = smoothScrollXCur = 0; smoothScrollYTimerCount = smoothScrollXTimerCount = 0; smoothYTimer = smoothXTimer = 0; } ScrlBkgWnd::~ScrlBkgWnd() { inDestroy = TRUE; } int ScrlBkgWnd::onInit() { SCRLBKGWND_PARENT::onInit(); scrollY = 0; scrollX = 0; hSep.setOrientation(SEP_HORIZONTAL); hScroll.setBitmaps(L"wasabi.scrollbar.horizontal.left", L"wasabi.scrollbar.horizontal.left.pressed", L"wasabi.scrollbar.horizontal.left.hover", L"wasabi.scrollbar.horizontal.right", L"wasabi.scrollbar.horizontal.right.pressed", L"wasabi.scrollbar.horizontal.right.hover", L"wasabi.scrollbar.horizontal.button", L"wasabi.scrollbar.horizontal.button.pressed", L"wasabi.scrollbar.horizontal.button.hover"); hScroll.setBackgroundBitmaps(L"wasabi.scrollbar.horizontal.background.left", L"wasabi.scrollbar.horizontal.background.middle", L"wasabi.scrollbar.horizontal.background.right"); vSep.setOrientation(SEP_VERTICAL); vScroll.setBitmaps(L"wasabi.scrollbar.vertical.left", L"wasabi.scrollbar.vertical.left.pressed", L"wasabi.scrollbar.vertical.left.hover", L"wasabi.scrollbar.vertical.right", L"wasabi.scrollbar.vertical.right.pressed", L"wasabi.scrollbar.vertical.right.hover", L"wasabi.scrollbar.vertical.button", L"wasabi.scrollbar.vertical.button.pressed", L"wasabi.scrollbar.vertical.button.hover"); vScroll.setBackgroundBitmaps(L"wasabi.scrollbar.vertical.background.top", L"wasabi.scrollbar.vertical.background.middle", L"wasabi.scrollbar.vertical.background.bottom"); // hScroll->setVertical(FALSE); vScroll.setVertical(TRUE); hScroll.setStartHidden(TRUE); // prevent showing window at creation vScroll.setStartHidden(TRUE); hSep.setStartHidden(TRUE); vSep.setStartHidden(TRUE); hScroll.setParent(this); vScroll.setParent(this); hSep.setParent(this); vSep.setParent(this); hScroll.init(getOsModuleHandle(), getOsWindowHandle()); vScroll.init(getOsModuleHandle(), getOsWindowHandle()); hSep.init(getOsModuleHandle(), getOsWindowHandle()); vSep.init(getOsModuleHandle(), getOsWindowHandle()); hScroll.setPosition(0); vScroll.setPosition(0); setSlidersPosition(); // position sliders and show them if needed return 1; } void ScrlBkgWnd::setBgBitmap(const wchar_t *b) { bmp = b; if (b) setBgColor(DEFAULT_BGCOLOR); } void ScrlBkgWnd::setBgColor(ARGB32 rgb) { bgColor = rgb; } SkinBitmap *ScrlBkgWnd::getBgBitmap(void) { return bmp; } ARGB32 ScrlBkgWnd::getBgColor(void) { return bgColor; } // Scroll to a specified Y-pixels void ScrlBkgWnd::scrollToY(int y, int signal) { WndCanvas *canvas = NULL; RECT r; int offset; int dor2 = 0; RECT r2 = {0, 0, 0, 0}; int focused = gotFocus(); if (isVirtual() || renderRatioActive()) { scrollY = y; invalidateRect(&clientRect()); onScrollY(y); return ; } if (!Wasabi::Std::Wnd::isValidWnd(getOsWindowHandle())) return ; // no need to paint if (y > scrollY) { // tree scrolling up, scroller going down. invalidating from the bottom. bitblting from bottom to top int lines = y - scrollY; offset = -lines; getClientRect(&r); canvas = new WndCanvas(); canvas->attachToClient(this); RegionI reg; makeWindowOverlayMask(®); RegionI clip(&r); reg.offset(0, offset); clip.subtractRegion(®); canvas->selectClipRgn(&clip); int b = hScroll.isVisible() ? hScroll.getHeight() : 0; int c = vScroll.isVisible() ? vScroll.getWidth() : 0; int a = focused && (!b); if (r.bottom-r.top-lines > 0) canvas->blit(r.left, r.top+lines, canvas, r.left, r.top, r.right-r.left- c, r.bottom-r.top-lines-a - b); // int a = focused && (!hScroll->isVisible()); //if (r.bottom - r.top - lines > 0) // canvas->blit(r.left, r.top + lines, canvas, r.left, r.top, r.right - r.left, r.bottom - r.top - lines - a); canvas->selectClipRgn(NULL); if (!clip.isEmpty()) invalidateRgn(&clip); getClientRect(&r2); r2.bottom = r2.top + 1; dor2 = 1; r.top = r.bottom - lines - 1; } if (y < scrollY) { // tree scrolling down, scroller going up. invalidating from the top. bitblting from top to bottom int lines = scrollY - y; offset = lines; getClientRect(&r); canvas = new WndCanvas(); canvas->attachToClient(this); RegionI reg; makeWindowOverlayMask(®); RegionI clip(&r); reg.offset(0, offset); clip.subtractRegion(®); canvas->selectClipRgn(&clip); int c = vScroll.isVisible() ? vScroll.getWidth() : 0; canvas->blit(r.left, r.top+focused, canvas, r.left, r.top+lines+focused, r.right-r.left-c, r.bottom-r.top-lines-focused); //canvas->blit(r.left, r.top + focused, canvas, r.left, r.top + lines + focused, r.right - r.left, r.bottom - r.top - lines - focused); canvas->selectClipRgn(NULL); if (!clip.isEmpty()) invalidateRgn(&clip); getClientRect(&r2); r2.top = r2.bottom - 1; dor2 = 1; r.bottom = r.top + lines + 1; } if (canvas) { delete canvas; scrollY = y; // in case we have a virtualCanvas, we need to tell BaseWnd to call us to paint on it next time it's needed coz we blited directly to the screen RECT cr; getClientRect(&cr); cr.top -= getHeaderHeight(); RECT screenblit; SubtractRect(&screenblit, &cr, &r); // invalidate what's needed if (dor2 && focused) cascadeRepaintRect(&r2, 0); cascadeRepaintRect(&r); deferedInvalidateRect(&screenblit); //dbbuffer = 1; //repaint(); } if (signal) updateVScroll(y); onScrollY(y); } // Scroll to a specified X-pixel void ScrlBkgWnd::scrollToX(int x, int signal) { WndCanvas *canvas = NULL; RECT r; int offset; int dor2 = 0; RECT r2 = {0, 0, 0, 0}; int focused = gotFocus(); if (isVirtual() || ABS(getRenderRatio() - 1.0) > 0.01) { scrollX = x; getClientRect(&r); invalidateRect(&r); return ; } if (x > scrollX) { // tree scrolling left, scroller going right. invalidating from the right. bitblting from right to left int lines = x - scrollX; offset = -lines; getClientRect(&r); r.top -= getHeaderHeight(); canvas = new WndCanvas(); canvas->attachToClient(this); RegionI reg; makeWindowOverlayMask(®); RegionI clip(&r); reg.offset(offset, 0); clip.subtractRegion(®); canvas->selectClipRgn(&clip); int c = vScroll.isVisible() ? vScroll.getWidth() : 0; canvas->blit(r.left+lines, r.top, canvas, r.left, r.top, r.right-r.left-lines-focused-c, r.bottom-r.top); //canvas->blit(r.left + lines, r.top, canvas, r.left, r.top, r.right - r.left - lines - focused, r.bottom - r.top); canvas->selectClipRgn(NULL); if (!reg.isEmpty()) invalidateRgn(®); getClientRect(&r2); r2.right = r2.left + 1; dor2 = 1; r.left = r.right - lines - 1; } if (x < scrollX) { // tree scrolling right, scroller going left. invalidating from the left. bitblting from left to right int lines = scrollX - x; offset = lines; getClientRect(&r); r.top -= getHeaderHeight(); canvas = new WndCanvas(); canvas->attachToClient(this); RegionI reg; makeWindowOverlayMask(®); RegionI clip(&r); reg.offset(offset, 0); clip.subtractRegion(®); canvas->selectClipRgn(&clip); int a = focused && (!vScroll.isVisible()); int c = hScroll.isVisible() ? hScroll.getHeight()-focused : 0; canvas->blit(r.left+a, r.top, canvas, r.left+lines, r.top, r.right-r.left-lines-a, r.bottom-r.top-c); //int a = focused && (!vScroll->isVisible()); // canvas->blit(r.left + a, r.top, canvas, r.left + lines, r.top, r.right - r.left - lines - a, r.bottom - r.top); canvas->selectClipRgn(NULL); if (!reg.isEmpty()) invalidateRgn(®); getClientRect(&r2); r2.left = r2.right - 1; dor2 = 1; r.right = r.left + lines + 1; } if (canvas) { delete canvas; scrollX = x; // in case we have a virtualCanvas, we need to tell BaseWnd to call us to paint on it next time it's needed coz we blited directly to the screen RECT cr; getClientRect(&cr); cr.top -= getHeaderHeight(); RECT screenblit; SubtractRect(&screenblit, &cr, &r); deferedInvalidateRect(&screenblit); if (dor2 && focused) cascadeRepaintRect(&r2, 0); // invalidate what's needed cascadeRepaintRect(&r); //dbbuffer = 1; //repaint(); } if (signal) updateHScroll(x); } void ScrlBkgWnd::setSlidersPosition() { if (in_set_slider_position) return ; in_set_slider_position = 1; _setSlidersPosition(); in_set_slider_position = 0; } void ScrlBkgWnd::_setSlidersPosition() { if (!isInited()) return ; RECT d; getClientRect(&d); if ((d.left >= d.right) || (d.top >= d.bottom)) return ; RECT r; if (inDestroy) return ; if (!isVisible()) { needSetSliders = TRUE; return ; } needSetSliders = FALSE; if (needHScroll()) { SCRLBKGWND_PARENT::getClientRect(&r); r.top = r.bottom - getScrollbarWidth(); if (needVScroll()) r.right -= getScrollbarWidth() + (wantsep ? SCROLLBAR_SEP : 0); RECT z; hScroll.getClientRect(&z); if (!Wasabi::Std::rectEqual(r, z)) { // assumes ScrollBars are virtual hScroll.resizeToRect(&r); RECT s = r; s.bottom = s.top; s.top -= (wantsep ? SCROLLBAR_SEP : 0); hSep.resizeToRect(&s); } if (!hScroll.isVisible()) { hScroll.setVisible(TRUE); if (wantsep) hSep.setVisible(TRUE); onHScrollToggle(1); } hScroll.setNPages(((int)(getContentsWidth() / (r.right - r.left))) + 1); hScroll.setUpDownValue((int)(((float)lineHeight / (getContentsWidth() - (r.right - r.left)))*SCROLLBAR_FULL)); hScroll.setPosition((int)((float)scrollX / getMaxScrollX() * SCROLLBAR_FULL)); } else { if (hScroll.isVisible()) { hScroll.setVisible(FALSE); if (wantsep) hSep.setVisible(FALSE); onHScrollToggle(0); } hScroll.setPosition(0); scrollToX(0); } if (needVScroll()) { SCRLBKGWND_PARENT::getClientRect(&r); r.left = r.right - getScrollbarWidth(); if (needHScroll()) r.bottom -= getScrollbarWidth(); RECT z; vScroll.getNonClientRect(&z); if (!Wasabi::Std::rectEqual(r, z)) { vScroll.resizeToRect(&r); RECT s = r; s.right = s.left; s.left -= (wantsep ? SCROLLBAR_SEP : 0); vSep.resizeToRect(&s); } if (!vScroll.isVisible()) { vScroll.setVisible(TRUE); if (wantsep) vSep.setVisible(TRUE); onVScrollToggle(1); } vScroll.setNPages(((int)(getContentsHeight() / (r.bottom - r.top))) + 1); vScroll.setUpDownValue((int)(((float)lineHeight / (getContentsHeight() - (r.bottom - r.top)))*SCROLLBAR_FULL)); vScroll.setPosition((int)((float)scrollY / getMaxScrollY() * SCROLLBAR_FULL)); } else { if (vScroll.isVisible()) { vScroll.setVisible(FALSE); if (wantsep) vSep.setVisible(FALSE); onVScrollToggle(0); } vScroll.setPosition(0); scrollToY(0); } hSep.invalidate(); vSep.invalidate(); if (needHScroll() && needVScroll()) { getNonClientRect(&smsqr); smsqr.left = smsqr.right - getScrollbarWidth(); smsqr.top = smsqr.bottom - getScrollbarWidth(); invalidateRect(&smsqr); } else ZERO(smsqr); } void ScrlBkgWnd::onHScrollToggle(int set) {} void ScrlBkgWnd::onVScrollToggle(int set) {} int ScrlBkgWnd::onPaint(Canvas *canvas) { RECT d; getClientRect(&d); if (d.right > d.left + 0xFFFF || d.bottom > d.top + 0xFFFF) return 1; if ((d.left >= d.right) || (d.top >= d.bottom)) { return SCRLBKGWND_PARENT::onPaint(canvas); } if (needSetSliders) setSlidersPosition(); // RECT z; // GetUpdateRect(gethWnd(), &z, FALSE); PaintCanvas paintcanvas; PaintBltCanvas paintbcanvas; if (canvas == NULL) { if (dbbuffer) { if (!paintbcanvas.beginPaintNC(this)) return 0; canvas = &paintbcanvas; } else { if (!paintcanvas.beginPaint(this)) return 0; canvas = &paintcanvas; } } //dbbuffer=1; SCRLBKGWND_PARENT::onPaint(canvas); RegionI *smsq = NULL; if (needHScroll() && needVScroll()) { renderBaseTexture(canvas, smsqr); smsq = new RegionI(&smsqr); } RECT r; LabelWnd::getNonClientRect(&r); RECT c = {r.left, r.top, r.right, r.top + getLabelHeight()}; // create label rect RegionI *clip = new RegionI(); if (canvas->getClipRgn(clip) == 0) { delete clip; clip = new RegionI(&r); if (smsq) clip->subtractRegion(smsq); canvas->selectClipRgn(clip); } else { RegionI reg(&c); clip->subtractRegion(®); if (smsq) clip->subtractRegion(smsq); canvas->selectClipRgn(clip); } delete smsq; drawBackground(canvas); delete clip; if (getRenderRatio() != lastratio) { invalidate(); lastratio = getRenderRatio(); } // todo: make that an event return 1; } int ScrlBkgWnd::needDoubleBuffer() { return dbbuffer; } int ScrlBkgWnd::onEraseBkgnd(HDC dc) { /* DCCanvas canvas; canvas.cloneDC(dc); drawBackground(&canvas);*/ return 1; } // Draws tiled background void ScrlBkgWnd::drawBackground(Canvas *canvas) { RECT r(clientRect()); RegionI reg(&r); RegionI old; canvas->getClipRgn(&old); reg.andRegion(&old); canvas->selectClipRgn(®); if (bmp.getBitmap() && bgColor == DEFAULT_BGCOLOR) { r.top -= scrollY % bmp.getBitmap()->getHeight(); r.left -= scrollX % bmp.getBitmap()->getWidth(); if (wantTileBg) bmp.getBitmap()->blitTile(canvas, &r); else bmp.getBitmap()->stretchToRect(canvas, &r); } else if (bgColor != DEFAULT_BGCOLOR) { canvas->fillRect(&r, bgColor); } canvas->selectClipRgn(&old); } bool ScrlBkgWnd::needHScroll() { if (!wantHScroll()) return FALSE; RECT r; getNonClientRect(&r); if (vScroll.isVisible()) r.right -= getScrollbarWidth(); return (getContentsWidth() > r.right - r.left); } bool ScrlBkgWnd::needVScroll() { if (!wantVScroll()) return FALSE; RECT r; getNonClientRect(&r); r.top += getHeaderHeight(); if (hScroll.isVisible()) r.bottom -= getScrollbarWidth(); return (getContentsHeight() > r.bottom - r.top); } // Returns the current tree width in pixels int ScrlBkgWnd::getContentsWidth() { /*RECT r; ScrlBkgWnd::getClientRect(&r); return r.right-r.left;*/ return 10000; } // Returns the current tree height in pixels int ScrlBkgWnd::getContentsHeight() { /*RECT r; ScrlBkgWnd::getClientRect(&r); return r.bottom-r.top;*/ return 10000; } int ScrlBkgWnd::getMaxScrollY() { RECT r; getClientRect(&r); return MAX(0, getContentsHeight() - (r.bottom - r.top)); } int ScrlBkgWnd::getMaxScrollX() { RECT r; getClientRect(&r); return MAX(0, getContentsWidth() - (r.right - r.left)); } void ScrlBkgWnd::updateVScroll(int y) { if (getMaxScrollY() == 0) { vScroll.setPosition(0); return ; } int z = (int)((float)y / getMaxScrollY() * SCROLLBAR_FULL); vScroll.setPosition(z); } void ScrlBkgWnd::updateHScroll(int x) { if (getMaxScrollX() == 0) { hScroll.setPosition(0); return ; } int z = (int)((float)x / getMaxScrollX() * SCROLLBAR_FULL); hScroll.setPosition(z); } void ScrlBkgWnd::updateScrollY(bool smooth) { if (getMaxScrollY() == 0) { scrollToY(0); return ; } int y = (int)((float)(vScroll.getPosition()) / SCROLLBAR_FULL * getMaxScrollY()); if (!smooth) scrollToY(y /*& ~3*/); else smoothScrollToY(y); } void ScrlBkgWnd::updateScrollX(bool smooth) { if (getMaxScrollX() == 0) { scrollToX(0); return ; } int x = (int)((float)(hScroll.getPosition()) / SCROLLBAR_FULL * getMaxScrollX()); if (!smooth) scrollToX(x /*& ~3*/); else smoothScrollToX(x); } void ScrlBkgWnd::smoothScrollToX(int x) { killSmoothXTimer(); smoothScrollXInc = -(float)(scrollX - x) / SMOOTH_STEPS; smoothScrollXCur = (float)scrollX; smoothScrollXTimerCount = 0; smoothXTimer = 1; setTimer(TIMER_SMOOTHSCROLLX, 25); } void ScrlBkgWnd::killSmoothYTimer() { if (smoothYTimer) { killTimer(TIMER_SMOOTHSCROLLY); smoothScrollYCur += smoothScrollYInc * (SMOOTH_STEPS - smoothScrollYTimerCount); scrollToY((int)smoothScrollYCur); smoothYTimer = 0; updateVScroll(scrollY); } } void ScrlBkgWnd::killSmoothXTimer() { if (smoothXTimer) { killTimer(TIMER_SMOOTHSCROLLX); smoothScrollXCur += smoothScrollXInc * (SMOOTH_STEPS - smoothScrollXTimerCount); scrollToX((int)smoothScrollXCur); smoothXTimer = 0; updateHScroll(scrollX); } } void ScrlBkgWnd::smoothScrollToY(int y) { killSmoothYTimer(); smoothScrollYInc = -(float)(scrollY - y) / SMOOTH_STEPS; smoothScrollYCur = (float)scrollY; smoothScrollYTimerCount = 0; smoothYTimer = 1; setTimer(TIMER_SMOOTHSCROLLY, 25); } void ScrlBkgWnd::timerCallback(int id) { switch (id) { case TIMER_SMOOTHSCROLLY: smoothScrollYCur += smoothScrollYInc; scrollToY((int)smoothScrollYCur, FALSE); if (++smoothScrollYTimerCount == SMOOTH_STEPS) killSmoothYTimer(); return ; case TIMER_SMOOTHSCROLLX: smoothScrollXCur += smoothScrollXInc; scrollToX((int)smoothScrollXCur, FALSE); if (++smoothScrollXTimerCount == SMOOTH_STEPS) killSmoothXTimer(); return ; } SCRLBKGWND_PARENT::timerCallback(id); } // Gets notification from sliders int ScrlBkgWnd::childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2) { switch (msg) { case ChildNotify::SCROLLBAR_SETPOSITION: if (child == &vScroll) { updateScrollY(!!param1); return 1; } if (child == &hScroll) { updateScrollX(!!param1); return 1; } break; } return SCRLBKGWND_PARENT::childNotify(child, msg, param1, param2); } int ScrlBkgWnd::onResize() { int rt = SCRLBKGWND_PARENT::onResize(); if (!isInited()) return rt; invalidateRect(&smsqr); setSlidersPosition(); return 1; } void ScrlBkgWnd::onSetVisible(int show) { SCRLBKGWND_PARENT::onSetVisible(show); if (show) setSlidersPosition(); } void ScrlBkgWnd::getClientRect(RECT *r) { SCRLBKGWND_PARENT::getClientRect(r); if (vScroll.isVisible(1)) r->right -= getScrollbarWidth() + (wantsep ? SCROLLBAR_SEP : 0); if (hScroll.isVisible(1)) r->bottom -= getScrollbarWidth() + (wantsep ? SCROLLBAR_SEP : 0); r->top += getHeaderHeight(); } /*void ScrlBkgWnd::getNonClientRect(RECT *r) { SCRLBKGWND_PARENT::getClientRect(r); // my non client rect is my parent's client rect return; }*/ int ScrlBkgWnd::getHeaderHeight() { return 0; } void ScrlBkgWnd::setLineHeight(int h) { lineHeight = h; } int ScrlBkgWnd::getLinesPerPage() { RECT r; getClientRect(&r); int h = r.bottom - r.top; return h / lineHeight; } int ScrlBkgWnd::getScrollX() { return scrollX; } int ScrlBkgWnd::getScrollY() { return scrollY; } int ScrlBkgWnd::getScrollbarWidth() { // TODO: maybe do if (hScroll.isVisible()) return hScroll.getWidth(); return vScroll.getWidth(); return 0; } /*void ScrlBkgWnd::clientToScreen(RECT *r) { POINT p; p.x = r->left; p.y = r->top; SCRLBKGWND_PARENT::clientToScreen((int *)&p.x, (int*)&p.y); r->left = p.x; r->top = p.y; p.x = r->right; p.y = r->bottom; SCRLBKGWND_PARENT::clientToScreen((int *)&p.x, (int*)&p.y); r->right = p.x; r->bottom = p.y; } void ScrlBkgWnd::clientToScreen(int *x, int *y) { SCRLBKGWND_PARENT::clientToScreen(x, y); } void ScrlBkgWnd::clientToScreen(POINT *p) { TREEWND_PARENT::clientToScreen((int *)&p->x, (int *)&p->y); }*/ void ScrlBkgWnd::makeWindowOverlayMask(api_region *r) { return ; #ifdef WIN32 // With this routine empty, I'm just nuking the code from x-plat builds < KP HDC dc = GetDC(getOsWindowHandle()); //if (getRandomRgn) { RECT cr; getClientRect(&cr); RECT wr; getWindowRect(&wr); RegionI sr; Wasabi::Std::Wnd::getRandomRegion(dc, sr.getOSHandle()); sr.offset( -wr.left, -wr.top); r->setRect(&cr); r->subtractRegion(&sr); } ReleaseDC(getOsWindowHandle(), dc); #endif }