#include "WidgetManager.h" #include "Widget.h" #include "Graphics.h" #include "Image.h" #include "KeyCodes.h" #include "DDImage.h" #include "SexyAppBase.h" #include "PerfTimer.h" #include "Debug.h" using namespace Sexy; using namespace std; WidgetManager::WidgetManager(SexyAppBase* theApp) { mApp = theApp; mMinDeferredOverlayPriority = 0x7FFFFFFF; mWidgetManager = this; mMouseIn = false; mDefaultTab = NULL; mImage = NULL; mLastHadTransients = false; mPopupCommandWidget = NULL; mFocusWidget = NULL; mLastDownWidget = NULL; mOverWidget = NULL; mBaseModalWidget = NULL; mDefaultBelowModalFlagsMod.mRemoveFlags = WIDGETFLAGS_ALLOW_MOUSE | WIDGETFLAGS_ALLOW_FOCUS; mWidth = 0; mHeight = 0; mHasFocus = true; mUpdateCnt = 0; mLastDownButtonId = 0; mDownButtons = 0; mActualDownButtons = 0; mWidgetFlags = WIDGETFLAGS_UPDATE | WIDGETFLAGS_DRAW | WIDGETFLAGS_CLIP | WIDGETFLAGS_ALLOW_MOUSE | WIDGETFLAGS_ALLOW_FOCUS; for (int i = 0; i < 0xFF; i++) mKeyDown[i] = false; } WidgetManager::~WidgetManager() { FreeResources(); } void WidgetManager::FreeResources() { } void WidgetManager::DisableWidget(Widget* theWidget) { if (mOverWidget == theWidget) { Widget* aOverWidget = mOverWidget; mOverWidget = NULL; MouseLeave(aOverWidget); } if (mLastDownWidget == theWidget) { Widget* aLastDownWidget = mLastDownWidget; mLastDownWidget = NULL; DoMouseUps(aLastDownWidget, mDownButtons); mDownButtons = 0; } if (mFocusWidget == theWidget) { Widget* aFocusWidget = mFocusWidget; mFocusWidget = NULL; aFocusWidget->LostFocus(); } if (mBaseModalWidget == theWidget) mBaseModalWidget = NULL; } int WidgetManager::GetWidgetFlags() { return mHasFocus ? mWidgetFlags : GetModFlags(mWidgetFlags, mLostFocusFlagsMod); } Widget* WidgetManager::GetAnyWidgetAt(int x, int y, int* theWidgetX, int* theWidgetY) { bool found; return GetWidgetAtHelper(x, y, GetWidgetFlags(), &found, theWidgetX, theWidgetY); } Widget* WidgetManager::GetWidgetAt(int x, int y, int* theWidgetX, int* theWidgetY) { Widget* aWidget = GetAnyWidgetAt(x, y, theWidgetX, theWidgetY); if ((aWidget != NULL) && (aWidget->mDisabled)) aWidget = NULL; return aWidget; } bool WidgetManager::IsLeftButtonDown() { return (mActualDownButtons&1)?true:false; } bool WidgetManager::IsMiddleButtonDown() { return (mActualDownButtons&4)?true:false; } bool WidgetManager::IsRightButtonDown() { return (mActualDownButtons&2)?true:false; } void WidgetManager::DoMouseUps() { if (mLastDownWidget!=NULL && mDownButtons!=0) { DoMouseUps(mLastDownWidget, mDownButtons); mDownButtons = 0; mLastDownWidget = NULL; } } void WidgetManager::DeferOverlay(Widget* theWidget, int thePriority) { mDeferredOverlayWidgets.push_back(std::pair(theWidget, thePriority)); if (thePriority < mMinDeferredOverlayPriority) mMinDeferredOverlayPriority = thePriority; } void WidgetManager::FlushDeferredOverlayWidgets(int theMaxPriority) { for (;;) { int aNextMinPriority = 0x7FFFFFFF; for (int i = 0; i < (int) mDeferredOverlayWidgets.size(); i++) { Widget* aWidget = mDeferredOverlayWidgets[i].first; if (aWidget != NULL) { int aPriority = mDeferredOverlayWidgets[i].second; if (aPriority == mMinDeferredOverlayPriority) { // Overlays don't get clipped Graphics g(*mCurG); g.Translate(-mMouseDestRect.mX, -mMouseDestRect.mY); g.Translate(aWidget->mX, aWidget->mY); g.SetFastStretch(!g.Is3D()); g.SetLinearBlend(g.Is3D()); aWidget->DrawOverlay(&g, aPriority); mDeferredOverlayWidgets[i].first = NULL; } else { if (aPriority < aNextMinPriority) aNextMinPriority = aPriority; } } } mMinDeferredOverlayPriority = aNextMinPriority; if (aNextMinPriority == 0x7FFFFFFF) { // No more widgets lined up for overlays, clear our vector mDeferredOverlayWidgets.resize(0); break; } // Lowest overlay priority is higher or equal to our current widget, // so continue deferring if (aNextMinPriority >= theMaxPriority) break; } } void WidgetManager::DoMouseUps(Widget* theWidget, ulong theDownCode) { int aClickCountTable[3] = { 1,-1, 3 }; for (int i = 0; i < 3; i++) { if ((theDownCode & (1 << i)) != 0) { theWidget->mIsDown = false; theWidget->MouseUp(mLastMouseX - theWidget->mX, mLastMouseY - theWidget->mY, aClickCountTable[i]); } } } void WidgetManager::RemapMouse(int& theX, int& theY) { theX = ( theX - mMouseSourceRect.mX ) * mMouseDestRect.mWidth / mMouseSourceRect.mWidth + mMouseDestRect.mX; theY = ( theY - mMouseSourceRect.mY ) * mMouseDestRect.mHeight / mMouseSourceRect.mHeight + mMouseDestRect.mY; } void WidgetManager::MouseEnter(Widget* theWidget) { theWidget->mIsOver = true; theWidget->MouseEnter(); if (theWidget->mDoFinger) theWidget->ShowFinger(true); } void WidgetManager::MouseLeave(Widget* theWidget) { theWidget->mIsOver = false; theWidget->MouseLeave(); if (theWidget->mDoFinger) theWidget->ShowFinger(false); } void WidgetManager::SetBaseModal(Widget* theWidget, const FlagsMod& theBelowFlagsMod) { mBaseModalWidget = theWidget; mBelowModalFlagsMod = theBelowFlagsMod; if ((mOverWidget != NULL) && (mBelowModalFlagsMod.mRemoveFlags & WIDGETFLAGS_ALLOW_MOUSE) && (IsBelow(mOverWidget, mBaseModalWidget))) { Widget* aWidget = mOverWidget; mOverWidget = NULL; MouseLeave(aWidget); } if ((mLastDownWidget != NULL) && (mBelowModalFlagsMod.mRemoveFlags & WIDGETFLAGS_ALLOW_MOUSE) && (IsBelow(mLastDownWidget, mBaseModalWidget))) { Widget* aWidget = mLastDownWidget; int aDownButtons = mDownButtons; mDownButtons = 0; mLastDownWidget = NULL; DoMouseUps(aWidget, aDownButtons); } if ((mFocusWidget != NULL) && (mBelowModalFlagsMod.mRemoveFlags & WIDGETFLAGS_ALLOW_FOCUS) && (IsBelow(mFocusWidget, mBaseModalWidget))) { Widget* aWidget = mFocusWidget; mFocusWidget = NULL; aWidget->LostFocus(); } } void WidgetManager::AddBaseModal(Widget* theWidget, const FlagsMod& theBelowFlagsMod) { PreModalInfo aPreModalInfo; aPreModalInfo.mBaseModalWidget = theWidget; aPreModalInfo.mPrevBaseModalWidget = mBaseModalWidget; aPreModalInfo.mPrevFocusWidget = mFocusWidget; aPreModalInfo.mPrevBelowModalFlagsMod = mBelowModalFlagsMod; mPreModalInfoList.push_back(aPreModalInfo); SetBaseModal(theWidget, theBelowFlagsMod); } void WidgetManager::AddBaseModal(Widget* theWidget) { AddBaseModal(theWidget, mDefaultBelowModalFlagsMod); } void WidgetManager::RemoveBaseModal(Widget* theWidget) { DBG_ASSERT(mPreModalInfoList.size() > 0); bool first = true; while (mPreModalInfoList.size() > 0) { PreModalInfo* aPreModalInfo = &mPreModalInfoList.back(); if ((first) && (aPreModalInfo->mBaseModalWidget != theWidget)) { // We don't remove it yet, because we want to restore // its keyboard focused widget and crap later return; } // If we removed a widget's self from pre-modal info before // then that means the dialog got removed out-of-order but we // deferred setting the state back until now bool done = (aPreModalInfo->mPrevBaseModalWidget != NULL) || (mPreModalInfoList.size() == 1); SetBaseModal(aPreModalInfo->mPrevBaseModalWidget, aPreModalInfo->mPrevBelowModalFlagsMod); if (mFocusWidget == NULL) { mFocusWidget = aPreModalInfo->mPrevFocusWidget; if (mFocusWidget != NULL) mFocusWidget->GotFocus(); } mPreModalInfoList.pop_back(); if (done) break; first = false; } } void WidgetManager::Resize(const Rect& theMouseDestRect, const Rect& theMouseSourceRect) { mWidth = theMouseDestRect.mWidth + 2 * theMouseDestRect.mX; mHeight = theMouseDestRect.mHeight + 2 * theMouseDestRect.mY; mMouseDestRect = theMouseDestRect; mMouseSourceRect = theMouseSourceRect; } void WidgetManager::SetFocus(Widget* aWidget) { if (aWidget==mFocusWidget) return; if (mFocusWidget != NULL) mFocusWidget->LostFocus(); if ((aWidget != NULL) && (aWidget->mWidgetManager == this)) { mFocusWidget = aWidget; if ((mHasFocus) && (mFocusWidget != NULL)) mFocusWidget->GotFocus(); } else mFocusWidget = NULL; } void WidgetManager::GotFocus() { if (!mHasFocus) { mHasFocus = true; if (mFocusWidget != NULL) mFocusWidget->GotFocus(); } } void WidgetManager::LostFocus() { if (mHasFocus) { mActualDownButtons = 0; for (int aKeyNum = 0; aKeyNum < 0xFF; aKeyNum++) { if (mKeyDown[aKeyNum]) KeyUp((KeyCode) aKeyNum); } mHasFocus = false; if (mFocusWidget != NULL) mFocusWidget->LostFocus(); } } void WidgetManager::InitModalFlags(ModalFlags* theModalFlags) { theModalFlags->mIsOver = mBaseModalWidget == NULL; theModalFlags->mOverFlags = GetWidgetFlags(); theModalFlags->mUnderFlags = GetModFlags(theModalFlags->mOverFlags, mBelowModalFlagsMod); } void WidgetManager::DrawWidgetsTo(Graphics* g) { mCurG = g; ModalFlags aModalFlags; InitModalFlags(&aModalFlags); WidgetList::iterator anItr = mWidgets.begin(); while (anItr != mWidgets.end()) { Widget* aWidget = *anItr; if (aWidget->mVisible) { Graphics aClipG(*g); aClipG.SetFastStretch(true); aClipG.Translate(aWidget->mX, aWidget->mY); aWidget->DrawAll(&aModalFlags, &aClipG); } ++anItr; } mCurG = NULL; } bool WidgetManager::DrawScreen() { SEXY_AUTO_PERF("WidgetManager::DrawScreen"); //DWORD start = timeGetTime(); ModalFlags aModalFlags; InitModalFlags(&aModalFlags); bool drewStuff = false; int aDirtyCount = 0; bool hasTransients = false; bool hasDirtyTransients = false; // Survey WidgetList::iterator anItr = mWidgets.begin(); while (anItr != mWidgets.end()) { Widget* aWidget = *anItr; if (aWidget->mDirty) aDirtyCount++; ++anItr; } mMinDeferredOverlayPriority = 0x7FFFFFFF; mDeferredOverlayWidgets.resize(0); Graphics aScrG(mImage); mCurG = &aScrG; DDImage* aDDImage = dynamic_cast(mImage); bool surfaceLocked = false; if (aDDImage != NULL) surfaceLocked = aDDImage->LockSurface(); if (aDirtyCount > 0) { Graphics g(aScrG); g.Translate(-mMouseDestRect.mX, -mMouseDestRect.mY); bool is3D = mApp->Is3DAccelerated(); WidgetList::iterator anItr = mWidgets.begin(); while (anItr != mWidgets.end()) { Widget* aWidget = *anItr; if (aWidget == mWidgetManager->mBaseModalWidget) aModalFlags.mIsOver = true; if ((aWidget->mDirty) && (aWidget->mVisible)) { Graphics aClipG(g); aClipG.SetFastStretch(!is3D); aClipG.SetLinearBlend(is3D); aClipG.Translate(aWidget->mX, aWidget->mY); aWidget->DrawAll(&aModalFlags, &aClipG); aDirtyCount++; drewStuff = true; aWidget->mDirty = false; } ++anItr; } } FlushDeferredOverlayWidgets(0x7FFFFFFF); if (aDDImage != NULL && surfaceLocked) aDDImage->UnlockSurface(); mCurG = NULL; return drewStuff; } bool WidgetManager::UpdateFrame() { SEXY_AUTO_PERF("WidgetManager::UpdateFrame"); ModalFlags aModalFlags; InitModalFlags(&aModalFlags); // Keep us from having mLastWMUpdateCount interfere with our own updating mUpdateCnt++; mLastWMUpdateCount = mUpdateCnt; UpdateAll(&aModalFlags); return mDirty; } bool WidgetManager::UpdateFrameF(float theFrac) { SEXY_AUTO_PERF("WidgetManager::UpdateFrame"); ModalFlags aModalFlags; InitModalFlags(&aModalFlags); UpdateFAll(&aModalFlags, theFrac); return mDirty; } void WidgetManager::SetPopupCommandWidget(Widget* theList) { mPopupCommandWidget = theList; AddWidget(mPopupCommandWidget); } void WidgetManager::RemovePopupCommandWidget() { if (mPopupCommandWidget != NULL) { Widget *aWidget = mPopupCommandWidget; mPopupCommandWidget = NULL; RemoveWidget(aWidget); } } void WidgetManager::MousePosition(int x, int y) { int aLastMouseX = mLastMouseX; int aLastMouseY = mLastMouseY; mLastMouseX = x; mLastMouseY = y; int aWidgetX; int aWidgetY; Widget* aWidget = GetWidgetAt(x, y, &aWidgetX, &aWidgetY); if (aWidget != mOverWidget) { Widget* aLastOverWidget = mOverWidget; mOverWidget = NULL; if (aLastOverWidget != NULL) MouseLeave(aLastOverWidget); mOverWidget = aWidget; if (aWidget != NULL) { MouseEnter(aWidget); aWidget->MouseMove(aWidgetX, aWidgetY); } } else if ((aLastMouseX != x) || (aLastMouseY != y)) { if (aWidget != NULL) aWidget->MouseMove(aWidgetX, aWidgetY); } } void WidgetManager::RehupMouse() { if (mLastDownWidget != NULL) { if (mOverWidget != NULL) { Widget* aWidgetOver = GetWidgetAt(mLastMouseX, mLastMouseY, NULL, NULL); if (aWidgetOver != mLastDownWidget) { Widget* anOverWidget = mOverWidget; mOverWidget = NULL; MouseLeave(anOverWidget); } } } else if (mMouseIn) MousePosition(mLastMouseX, mLastMouseY); } bool WidgetManager::MouseUp(int x, int y, int theClickCount) { mLastInputUpdateCnt = mUpdateCnt; int aMask; if (theClickCount < 0) aMask = 0x02; else if (theClickCount == 3) aMask = 0x04; else aMask = 0x01; // Make sure that we thought this button was down anyway - possibly not, if we // disabled the widget already or something mActualDownButtons &= ~aMask; if ((mLastDownWidget != NULL) && ((mDownButtons & aMask) != 0)) { Widget* aLastDownWidget = mLastDownWidget; mDownButtons &= ~aMask; if (mDownButtons == 0) mLastDownWidget = NULL; aLastDownWidget->mIsDown = false; aLastDownWidget->MouseUp(x - aLastDownWidget->mX, y - aLastDownWidget->mY, theClickCount); } else mDownButtons &= ~aMask; MousePosition(x, y); return true; } bool WidgetManager::MouseDown(int x, int y, int theClickCount) { mLastInputUpdateCnt = mUpdateCnt; if (theClickCount < 0) mActualDownButtons |= 0x02; else if (theClickCount == 3) mActualDownButtons |= 0x04; else mActualDownButtons |= 0x01; MousePosition(x, y); if ((mPopupCommandWidget != NULL) && (!mPopupCommandWidget->Contains(x, y))) RemovePopupCommandWidget(); int aWidgetX; int aWidgetY; Widget* aWidget = GetWidgetAt(x, y, &aWidgetX, &aWidgetY); // Begin mouse down options /* // Option 1 //This code sets a new widget as the mouse drag focus widget and lets the old //mousedownwidget think the buttons popped up. if ((mLastDownWidget != NULL) && (mLastDownWidget != aWidget)) { DoMouseUps(mLastDownWidget, mDownButtons); mDownButtons = 0; } */ // Option 2 // This code passes all button downs to the mLastDownWidget if (mLastDownWidget != NULL) aWidget = mLastDownWidget; // End mouse down options if (theClickCount < 0) { mLastDownButtonId = -1; mDownButtons |= 0x02; } else if (theClickCount == 3) { mLastDownButtonId = 2; mDownButtons |= 0x04; } else { mLastDownButtonId = 1; mDownButtons |= 0x01; } mLastDownWidget = aWidget; if (aWidget != NULL) { if (aWidget->WantsFocus()) SetFocus(aWidget); aWidget->mIsDown = true; aWidget->MouseDown(aWidgetX, aWidgetY, theClickCount); } return true; } bool WidgetManager::MouseMove(int x, int y) { mLastInputUpdateCnt = mUpdateCnt; if (mDownButtons) return MouseDrag(x,y); mMouseIn = true; MousePosition(x, y); return true; } bool WidgetManager::MouseDrag(int x, int y) { mLastInputUpdateCnt = mUpdateCnt; mMouseIn = true; mLastMouseX = x; mLastMouseY = y; if ((mOverWidget != NULL) && (mOverWidget != mLastDownWidget)) { Widget* anOverWidget = mOverWidget; mOverWidget = NULL; MouseLeave(anOverWidget); } if (mLastDownWidget != NULL) { Point anAbsPos = mLastDownWidget->GetAbsPos(); int aWidgetX = x - anAbsPos.mX; int aWidgetY = y - anAbsPos.mY; mLastDownWidget->MouseDrag(aWidgetX, aWidgetY); Widget* aWidgetOver = GetWidgetAt(x, y, NULL, NULL); if ((aWidgetOver == mLastDownWidget) && (aWidgetOver != NULL)) { if (mOverWidget == NULL) { mOverWidget = mLastDownWidget; MouseEnter(mOverWidget); } } else { if (mOverWidget != NULL) { Widget* anOverWidget = mOverWidget; mOverWidget = NULL; MouseLeave(anOverWidget); } } } return true; } bool WidgetManager::MouseExit(int x, int y) { mLastInputUpdateCnt = mUpdateCnt; mMouseIn = false; if (mOverWidget != NULL) { MouseLeave(mOverWidget); mOverWidget = NULL; } return true; } void WidgetManager::MouseWheel(int theDelta) { mLastInputUpdateCnt = mUpdateCnt; if (mFocusWidget != NULL) mFocusWidget->MouseWheel(theDelta); } bool WidgetManager::KeyChar(SexyChar theChar) { mLastInputUpdateCnt = mUpdateCnt; if (theChar == KEYCODE_TAB) { //TODO: Check thing if (mKeyDown[KEYCODE_CONTROL]) { if (mDefaultTab != NULL) mDefaultTab->KeyChar(theChar); return true; } } if (mFocusWidget != NULL) mFocusWidget->KeyChar(theChar); return true; } bool WidgetManager::KeyDown(KeyCode key) { mLastInputUpdateCnt = mUpdateCnt; if ((key >= 0) && (key < 0xFF)) mKeyDown[key] = true; if (mFocusWidget != NULL) mFocusWidget->KeyDown(key); return true; } bool WidgetManager::KeyUp(KeyCode key) { mLastInputUpdateCnt = mUpdateCnt; if ((key >= 0) && (key < 0xFF)) mKeyDown[key] = false; if ((key == KEYCODE_TAB) && (mKeyDown[KEYCODE_CONTROL])) return true; if (mFocusWidget != NULL) mFocusWidget->KeyUp(key); return true; }