/*---------------------------------------------------------------------------* Copyright (C) 2010-2012 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ // Creates a window manager WindowManager::WindowManager(Peripheral* peripheral, GX2Texture** textureList, float screenWidth) : dummyWindow(this, "", CVec2(0, 0), CVec2(0, 0)), windowOver(-1), windowActive(-1), windowDragging(false), oldWindowX(0), oldWindowY(0), cursorTime(0), tabIndex(NULL), tabSubIndex(-1), tabX(0), tabY(0) { this->peripheral = peripheral; // Get all the textures if (textureList) memcpy(this->textureList, textureList, sizeof(this->textureList)); else memset(this->textureList, NULL, sizeof(this->textureList)); this->screenWidth = screenWidth; } // Initialization in case we want to reinitialize something void WindowManager::SetPeripheral(Peripheral* peripheral) { this->peripheral = peripheral; } // Initialization in case we want to reinitialize something void WindowManager::SetTextureList(GX2Texture** textureList) { memcpy(this->textureList, textureList, sizeof(this->textureList)); } // Initialization in case we want to reinitialize something void WindowManager::SetScreenWidth(float screenWidth) { this->screenWidth = screenWidth; } // Updates the input for this window manager void WindowManager::UpdateInput() { tabX = 0; tabY = 0; // This is all we need for held functionality cursorTime ++; if (peripheral->PositiveTriggered()) cursorTime = 0; // +Control Pad int dX = peripheral->RightPressed() - peripheral->LeftPressed(); int dY = peripheral->UpPressed() - peripheral->DownPressed(); if ((dX || dY) && tabSubIndex == -1) { if (!peripheral->UsingPad()) { peripheral->CursorX() = 0; peripheral->CursorY() = 0; peripheral->UsingPad() = true; } peripheral->CursorX() += dX * CURSOR_SPEED; peripheral->CursorY() += dY * CURSOR_SPEED; if (peripheral->CursorX() < -screenWidth) peripheral->CursorX() = -screenWidth; else if (peripheral->CursorX() > screenWidth) peripheral->CursorX() = screenWidth; if (peripheral->CursorY() < -1.0) peripheral->CursorY() = -1.0; else if (peripheral->CursorY() > 1.0) peripheral->CursorY() = 1.0; tabIndex = NULL; tabSubIndex = -1; return; } if (!peripheral->UsingPad()) { tabIndex = NULL; tabSubIndex = -1; } // Tabbing controls if (tabIndex) { if (peripheral->TabRightTriggered()) { tabIndex = tabIndex->next; tabSubIndex = -1; ForceValidTabForward(); return; } if (peripheral->TabLeftTriggered()) { tabIndex = tabIndex->prev; tabSubIndex = -1; ForceValidTabBack(); return; } } else { if (peripheral->TabRightTriggered() || peripheral->TabLeftTriggered()) { tabIndex = &dummyWindow.dummyTab; tabSubIndex = -1; ForceValidTabForward(); peripheral->UsingPad() = true; peripheral->UsingWiimote() = false; return; } } // Special tabbing controls if (dX || dY) { // Can't be handled here, so let something else (combo box) handle it tabX = dX; tabY = dY; peripheral->UsingPad() = true; return; } } // Constants for the close button const float CLOSEBUTTON_X = WINDOW_HANDLE_HEIGHT / 2; const float CLOSEBUTTON_Y = WINDOW_HANDLE_HEIGHT / 2; const float CLOSEBUTTON_WIDTH = WINDOW_HANDLE_HEIGHT * 0.8; const float CLOSEBUTTON_HEIGHT = WINDOW_HANDLE_HEIGHT * 0.8; // A special update for the close button bool WindowManager::UpdateCloseButton(Window* window) { if (!window->canKill) return false; int status; // We don't care as much if the cursor isn't being triggered if (peripheral->PositiveTriggered()) status = 2; else status = 1; // See if we just hit something if (peripheral->CursorColliding(window, window->GetWidth() / 2 - CLOSEBUTTON_X, window->GetTop() + CLOSEBUTTON_Y - window->GetY(), CLOSEBUTTON_WIDTH, CLOSEBUTTON_HEIGHT, true)) window->closeStatus = status; else window->closeStatus = 0; if (window->closeStatus == 2) return true; return false; } // A special draw for the close button void WindowManager::DrawCloseButton(Window* window) { if (!window->canKill) return; CVec4 color = BlackLighter(window->closeStatus); CVec4 color2 = WhiteDarker(window->closeStatus); peripheral->DrawBox(CVec3(window->GetX() + window->GetWidth() / 2 - CLOSEBUTTON_X, window->GetTop() + CLOSEBUTTON_Y, 0.999), CVec2(CLOSEBUTTON_WIDTH, CLOSEBUTTON_HEIGHT), color); peripheral->DrawTextCenter("X", CVec3(window->GetX() + window->GetWidth() / 2 - CLOSEBUTTON_X, window->GetTop() + CLOSEBUTTON_Y, 0.9991), CVec2(CLOSEBUTTON_WIDTH, CLOSEBUTTON_HEIGHT), color2); } // Sorts the list by z value void SortList(std::list& list) { // Bubble sort! for (std::list::iterator iter = list.begin(); iter != list.end(); ++iter) { std::list::iterator iter2 = iter; ++iter2; for (; iter2 != list.end(); ++iter2) { if ((*iter)->z < (*iter2)->z) { MenuItem* temp = *iter2; *iter2 = *iter; *iter = temp; } } } } // Draws the windows void WindowManager::Draw() { // This is how the manager typically draws! GX2SetDepthOnlyControl(GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_LESS); for (int i = windowList.size() - 1; i >= 0; --i) { Window* window = windowList[windowOrder[i]]; if (window->visible && !window->master) { // First, draw the window quad and title peripheral->DrawWindowQuad(CVec2(window->GetX(), window->GetTop()), CVec2(window->GetWidth(), window->GetHeight()), i == 0, window->Enabled(), CVec3(0.1, 0.1, 0.1), window->GetName()); // Then draw the close button DrawCloseButton(window); // Sort them by z! window->sortedDrawItems = window->menuItems; SortList(window->sortedDrawItems); // Then draw the window and its items window->PreDraw(); window->Draw(); for (std::list::reverse_iterator iter = window->sortedDrawItems.rbegin(); iter != window->sortedDrawItems.rend(); ++iter) { if ((*iter)->visible) (*iter)->Draw(); } // Then draw the child window float y = window->y - window->height; Window* custom = window->child; while (custom) { // Sort them by z! custom->sortedDrawItems = custom->menuItems; SortList(custom->sortedDrawItems); // Drawn at an offset! custom->x = window->x; custom->y = y; custom->offY = window->offY; custom->Draw(); for (std::list::reverse_iterator iter = custom->sortedDrawItems.rbegin(); iter != custom->sortedDrawItems.rend(); ++iter) { if ((*iter)->visible) (*iter)->Draw(); } y -= custom->maxHeight; custom = custom->child; } } } // The cursor! peripheral->DrawTexQuad(*GetTexture(CW_TEXTURE_CURSOR), CVec3(peripheral->CursorX(), peripheral->CursorY(), 1.0), CVec2(0.25, 0.25), CVec4(1.0, 1.0, 1.0, 1.0)); } // Updates the windows void WindowManager::Update(bool canBeOver) { this->canBeOver = canBeOver; UpdateInput(); if (tabIndex) BringWindowToFront(tabIndex->window); bool overCore[MAX_WINDOWS] = {0}; bool overHandle[MAX_WINDOWS] = {0}; if (!peripheral->PositivePressed() || tabIndex) windowDragging = false; int oldWindowActive = windowActive; // Drag the window! if (windowDragging) { Window* window = windowList[windowOrder[0]]; window->x += peripheral->CursorX() - oldWindowX; window->y += peripheral->CursorY() - oldWindowY; oldWindowX = peripheral->CursorX(); oldWindowY = peripheral->CursorY(); } // Figure out which window we're over windowOver = -1; for (int i = 0; i < windowList.size(); ++i) { Window* window = windowList[windowOrder[i]]; if (!window->visible || window->master) continue; overCore[windowOrder[i]] = window->CursorOver(); overHandle[windowOrder[i]] = peripheral->CursorColliding(window, window->x - window->GetX(), window->y + WINDOW_HANDLE_HEIGHT / 2 - window->GetY(), window->GetWidth(), WINDOW_HANDLE_HEIGHT, true); if (overCore[windowOrder[i]] || overHandle[windowOrder[i]]) { windowOver = windowOrder[i]; if (!window->enabled) windowOver = -1; break; } } // Move the window to the front! if (peripheral->PositiveTriggered()) { BringWindowToFront(); if (windowActive != -1 && !overCore[windowActive] && windowList[windowActive]->Active()) { oldWindowX = peripheral->CursorX(); oldWindowY = peripheral->CursorY(); windowDragging = true; } } if (windowActive != oldWindowActive && oldWindowActive != -1) windowList[oldWindowActive]->Reset(); bool canUpdate = true; std::vector windows(windowList.size()); std::vector bools(windowList.size()); for (int i = 0; i < windowList.size(); ++i) { windows[i] = windowList[windowOrder[i]]; bools[i] = (overCore[windowOrder[i]] || overHandle[windowOrder[i]]); } for (int i = 0; i < windows.size(); ++i) { Window* window = windows[i]; if (window->Active() && !window->master) { // Check for close button activity if (canUpdate && UpdateCloseButton(window)) { window->Hide(); windowDragging = false; canUpdate = false; continue; } // Then update the window and its items canUpdate = window->Update(canUpdate); // Sort them by z! window->sortedItems = window->menuItems; SortList(window->sortedItems); // Then update the child windows float y = window->y - window->height; Window* custom = window->child; while (custom) { // Update at an offset! custom->x = window->x; custom->y = y; custom->offY = window->offY; canUpdate = custom->Update(canUpdate); // Sort them by z! custom->sortedItems = custom->menuItems; SortList(custom->sortedItems); y -= custom->maxHeight; custom = custom->child; } if (canUpdate) { Window* custom = window; while (custom) { std::list::iterator iter = custom->sortedItems.begin(); std::list::iterator preiter = custom->sortedItems.begin(); // If an object has priority, it can block the ones after it! for ( ; preiter != custom->sortedItems.end(); ++preiter) { if ((*preiter)->status >= 2 && (*preiter)->visible) { canUpdate = (*preiter)->Update(); goto HIGH_PRIORITY_OBJECT_DONE; } } custom = custom->child; } // We're done with the focused object! HIGH_PRIORITY_OBJECT_DONE: canUpdate = window->PreUpdate(canUpdate); custom = window; while (custom) { std::list::iterator iter = custom->sortedItems.begin(); if (canUpdate) { // If the cursor is over an object, it can block the ones after it! for ( ; iter != custom->sortedItems.end(); ++iter) { if ((*iter)->status < 2 && (*iter)->status != -1 && (*iter)->visible && !(*iter)->Update()) { canUpdate = false; ++iter; break; } } } // The rest just get reset for ( ; iter != custom->sortedItems.end(); ++iter) { if ((*iter)->status < 2) (*iter)->Reset(); } custom = custom->child; } } else { // Do the PreUpdate, but with false instead of true! window->PreUpdate(false); // Reset all the objects... std::list::iterator iter = window->sortedItems.begin(); for ( ; iter != window->sortedItems.end(); ++iter) (*iter)->Reset(); // Including the ones in child windows! Window* custom = window->child; while (custom) { std::list::iterator iter = custom->sortedItems.begin(); for ( ; iter != custom->sortedItems.end(); ++iter) (*iter)->Reset(); custom = custom->child; } } if (bools[i]) canUpdate = false; } } // Clean up any windows that need deleted for (int i = 0; i < windowList.size(); ++i) { if (windowList[i]->Deleted()) { delete windowList[i]; --i; } } // Now update the tabbing! UpdateTabbing(); } // Brings a specific window to the front void WindowManager::BringWindowToFront(Window* window) { while (window->master) window = window->master; windowOver = window->number; BringWindowToFront(); } // Actually reorders the windows to put the active one in front void WindowManager::BringWindowToFront() { windowActive = windowOver; for (int i = windowList.size() - 2; i >= 0; --i) { if (windowOver == windowOrder[i + 1]) { windowOrder[i + 1] = windowOrder[i]; windowOrder[i] = windowOver; } } } // Adds the window to the list of windows void WindowManager::AddToWindowList(Window* window) { if (window->manager) window->manager->RemoveFromWindowList(window); window->number = windowList.size(); windowList.push_back(window); windowOrder.push_back(window->number); window->manager = this; } // Removes the window from the list of windows void WindowManager::RemoveFromWindowList(Window* window) { if (!window || window->manager != this) return; window->Hide(); windowList.erase(windowList.begin() + window->number); for (int i = 0; i < windowOrder.size(); ++i) { if (windowOrder[i] > window->number) windowOrder[i] --; else if (windowOrder[i] == window->number) { windowOrder.erase(windowOrder.begin() + i); --i; } } for (int i = window->number; i < windowList.size(); ++i) windowList[i]->number --; window->manager = NULL; if (windowActive == window->number) { windowActive = -1; windowOver = -1; tabIndex = NULL; tabSubIndex = -1; } if (windowOver == window->number) { windowOver = -1; tabIndex = NULL; tabSubIndex = -1; } } // A function to get the texture, in case it changes at some point GX2Texture** WindowManager::GetTexture(CWTextures num) { return &this->textureList[num]; } // A function to get the texture list itself GX2Texture** WindowManager::GetTextureList() { return this->textureList; }