From 191c85a63f96573287ce0d2173026f6646c3898c Mon Sep 17 00:00:00 2001 From: jsalmon3 <> Date: Sun, 23 May 2004 19:16:32 +0000 Subject: [PATCH] Pie menus from feb --- doc/scripts/ui.html | 12 +++ src/include/cursor.h | 1 + src/include/interface.h | 5 ++ src/include/ui.h | 6 ++ src/stratagus/mainloop.cpp | 1 + src/ui/botpanel.cpp | 58 +++++++----- src/ui/icons.cpp | 2 +- src/ui/mouse.cpp | 179 +++++++++++++++++++++++++++++++++++-- src/ui/script_ui.cpp | 51 +++++++++++ src/ui/ui.cpp | 8 ++ 10 files changed, 292 insertions(+), 31 deletions(-) diff --git a/doc/scripts/ui.html b/doc/scripts/ui.html index 4f082932f..81cc0accc 100644 --- a/doc/scripts/ui.html +++ b/doc/scripts/ui.html @@ -894,6 +894,18 @@ FIXME </dd> </dl> </dd> +<dt>"pie-menu", {tag, value, ...}</dt> +<dd> +<dl> + <dt>"radius", radius</dt> + <dd>The radius in pixels of the pie menu.</dd> + <dt>"file", "filename"</dt> + <dd>The image file for the background of the pie menu.</dd> + <dt>"mouse-button", "buttonname"</dt> + <dd>Which mouse button pops up the pie menu. Can be "right", "middle" or "left". + </dd> +</dl> +</dd> <dt>"map-area", {"pos", {x, y}, size, {w, h}}</dt> <dd>FIXME</dd> <dt>"menu-panel", {tag, value}</dt> diff --git a/src/include/cursor.h b/src/include/cursor.h index 99ae2144e..73de66b26 100644 --- a/src/include/cursor.h +++ b/src/include/cursor.h @@ -166,6 +166,7 @@ typedef enum _cursor_states_ { CursorStatePoint, ///< Normal cursor CursorStateSelect, ///< Select position CursorStateRectangle, ///< Rectangle selecting + CursorStatePieMenu, ///< Displaying Pie Menu } CursorStates; /*---------------------------------------------------------------------------- diff --git a/src/include/interface.h b/src/include/interface.h index 40a841499..cc034eaff 100644 --- a/src/include/interface.h +++ b/src/include/interface.h @@ -179,6 +179,7 @@ enum _key_modifiers_ { /// pressed mouse button flags enum _mouse_buttons_ { + NoButton = 0, ///< No button LeftButton = 2, ///< Left button on mouse MiddleButton = 4, ///< Middle button on mouse RightButton = 8, ///< Right button on mouse @@ -390,6 +391,10 @@ extern void DrawTimer(void); extern void UpdateTimer(void); /// Draw the unit button panel extern void DrawButtonPanel(void); + /// Update the status line with hints from the button +extern void UpdateStatusLineForButton(const ButtonAction*); + /// Draw the Pie Menu +extern void DrawPieMenu(void); /// Update the content of the unit button panel extern void UpdateButtonPanel(void); /// Handle button click in button panel area diff --git a/src/include/ui.h b/src/include/ui.h index 7c8dc76b8..749752847 100644 --- a/src/include/ui.h +++ b/src/include/ui.h @@ -249,6 +249,12 @@ typedef struct _ui_ { int ButtonPanelY; ///< Button panel screen Y position int CommandKeyFont; ///< Command key font + // Pie Menu + GraphicConfig PieMenuBackground; ///< Optional background image for the piemenu + enum _mouse_buttons_ PieMouseButton; ///< Which mouse button pops up the piemenu. Deactivate with the NoButton value. + int PieX[8]; ///< X position of the pies + int PieY[8]; ///< Y position of the pies + // Map area ViewportMode ViewportMode; ///< Current viewport mode Viewport* MouseViewport; ///< Viewport containing mouse diff --git a/src/stratagus/mainloop.cpp b/src/stratagus/mainloop.cpp index 738f2d497..b421772b1 100644 --- a/src/stratagus/mainloop.cpp +++ b/src/stratagus/mainloop.cpp @@ -376,6 +376,7 @@ global void UpdateDisplay(void) DrawTimer(); } + DrawPieMenu(); // draw pie menu only if needed DrawMenu(CurrentMenu); DrawAnyCursor(); diff --git a/src/ui/botpanel.cpp b/src/ui/botpanel.cpp index 2a8e3a83d..2f4de3b7e 100644 --- a/src/ui/botpanel.cpp +++ b/src/ui/botpanel.cpp @@ -220,7 +220,6 @@ global void DrawButtonPanel(void) int i; int v; Player* player; - const UnitStats* stats; const ButtonAction* buttons; char buf[8]; @@ -351,29 +350,7 @@ global void DrawButtonPanel(void) // if (ButtonAreaUnderCursor == ButtonAreaButton && ButtonUnderCursor == i && KeyState != KeyStateInput) { - SetStatusLine(buttons[i].Hint); - // FIXME: Draw costs - v = buttons[i].Value; - switch (buttons[i].Action) { - case ButtonBuild: - case ButtonTrain: - case ButtonUpgradeTo: - // FIXME: store pointer in button table! - stats = &UnitTypes[v]->Stats[player->Player]; - - SetCosts(0, UnitTypes[v]->Demand, stats->Costs); - break; - case ButtonResearch: - SetCosts(0, 0, Upgrades[v].Costs); - break; - case ButtonSpellCast: - SetCosts(SpellTypeTable[v]->ManaCost, 0, NULL); - break; - - default: - ClearCosts(); - break; - } + UpdateStatusLineForButton(&buttons[i]); } // @@ -403,6 +380,39 @@ global void DrawButtonPanel(void) } } +/** +** Update the status line with hints from the button +** +** @param button Button +*/ +global void UpdateStatusLineForButton(const ButtonAction* button) +{ + int v; + const UnitStats* stats; + + SetStatusLine(button->Hint); + // FIXME: Draw costs + v = button->Value; + switch (button->Action) { + case ButtonBuild: + case ButtonTrain: + case ButtonUpgradeTo: + // FIXME: store pointer in button table! + stats = &UnitTypes[v]->Stats[ThisPlayer->Player]; + SetCosts(0, UnitTypes[v]->Demand, stats->Costs); + break; + case ButtonResearch: + SetCosts(0, 0, Upgrades[v].Costs); + break; + case ButtonSpellCast: + SetCosts(SpellTypeTable[v]->ManaCost, 0, NULL); + break; + default: + ClearCosts(); + break; + } +} + /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ diff --git a/src/ui/icons.cpp b/src/ui/icons.cpp index b017d20ca..5e0766df0 100644 --- a/src/ui/icons.cpp +++ b/src/ui/icons.cpp @@ -347,7 +347,7 @@ global const char* IdentOfIcon(const Icon* icon) global void DrawIcon(const Player* player, Icon* icon, int x, int y) { GraphicPlayerPixels(player, icon->Sprite); - VideoDraw(icon->Sprite, icon->Index, x, y); + VideoDrawClip(icon->Sprite, icon->Index, x, y); } /** diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp index d6281fec1..f0135a9f0 100644 --- a/src/ui/mouse.cpp +++ b/src/ui/mouse.cpp @@ -30,12 +30,16 @@ //@{ +#define ICON_SIZE_X (TheUI.ButtonButtons[0].Width) +#define ICON_SIZE_Y (TheUI.ButtonButtons[0].Height) + /*---------------------------------------------------------------------------- -- Includes ----------------------------------------------------------------------------*/ #include <stdio.h> #include <stdlib.h> +#include <ctype.h> #include "stratagus.h" #include "tileset.h" @@ -78,6 +82,7 @@ global enum _cursor_on_ CursorOn = CursorOnUnknown; /// Cursor on field /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ +local void HandlePieMenuMouseSelection(void); /** ** Cancel building cursor mode. @@ -657,6 +662,23 @@ global void UIHandleMouseMove(int x, int y) return; } + if (CursorState == CursorStatePieMenu && CursorOn == CursorOnMap) { + // in the map area + // make the piemenu "follow" the mouse + if (CursorX - CursorStartX > TheUI.PieX[2]) { + CursorStartX = CursorX - TheUI.PieX[2]; + } + if (CursorStartX - CursorX > TheUI.PieX[2]) { + CursorStartX = CursorX + TheUI.PieX[2]; + } + if (CursorStartY - CursorY > TheUI.PieY[4]) { + CursorStartY = CursorY + TheUI.PieY[4]; + } + if (CursorY - CursorStartY > TheUI.PieY[4]) { + CursorStartY = CursorY - TheUI.PieY[4]; + } + } + // // Move map. // @@ -1320,6 +1342,7 @@ local void UISelectStateButtonDown(unsigned button __attribute__((unused))) UpdateButtonPanel(); } + /** ** Called if mouse button pressed down. ** @@ -1374,6 +1397,16 @@ global void UIHandleButtonDown(unsigned button) return; } + if (CursorState == CursorStatePieMenu) { + if (CursorOn == CursorOnMap) { + HandlePieMenuMouseSelection(); + } else { + // Pie Menu canceled + CursorState = CursorStatePoint; + } + return; + } + // // Cursor is on the map area // @@ -1431,7 +1464,15 @@ global void UIHandleButtonDown(unsigned button) return; } - if (MouseButtons & LeftButton) { // enter select mode + if (MouseButtons & TheUI.PieMouseButton) { // enter pie menu + CursorStartX = CursorX; + CursorStartY = CursorY; + if (NumSelected && Selected[0]->Player == ThisPlayer && + CursorState == CursorStatePoint) { + CursorState = CursorStatePieMenu; + MustRedraw |= RedrawCursor; + } + } else if (MouseButtons & LeftButton) { // enter select mode CursorStartX = CursorX; CursorStartY = CursorY; CursorStartScrMapX = CursorStartX - TheUI.MouseViewport->X + @@ -1458,8 +1499,8 @@ global void UIHandleButtonDown(unsigned button) y = Viewport2MapY(TheUI.MouseViewport, CursorY); if (UnitUnderCursor && (unit = UnitOnMapTile(x, y)) && - !UnitUnderCursor->Type->Decoration) { - unit->Blink = 4; // if right click on building -- blink + !UnitUnderCursor->Type->Decoration) { + unit->Blink = 4; // if right click on building -- blink } else { // if not not click on building -- green cross if (ClickMissile) { MakeLocalMissile(MissileTypeByIdent(ClickMissile), @@ -1525,7 +1566,7 @@ global void UIHandleButtonDown(unsigned button) if (ButtonUnderCursor == 0 && NumSelected == 1) { PlayGameSound(GameSounds.Click.Sound, MaxSampleVolume); ViewportCenterViewpoint(TheUI.SelectedViewport, Selected[0]->X, - Selected[0]->Y, Selected[0]->IX + TileSizeX / 2, + Selected[0]->Y, Selected[0]->IX + TileSizeX / 2, Selected[0]->IY + TileSizeY / 2); } // @@ -1559,7 +1600,7 @@ global void UIHandleButtonDown(unsigned button) // clicked on researching button // } else if (ButtonAreaUnderCursor == ButtonAreaResearching) { - if (!GameObserve && !GamePaused && + if (!GameObserve && !GamePaused && PlayersTeamed(ThisPlayer->Player, Selected[0]->Player->Player)) { if (ButtonUnderCursor == 0 && NumSelected == 1) { DebugPrint("Cancel research %s\n" _C_ @@ -1628,6 +1669,18 @@ global void UIHandleButtonUp(unsigned button) return; } + // + // Pie Menu + // + if (CursorState == CursorStatePieMenu) { + if (CursorStartX == CursorX && CursorStartY == CursorY) { + // no move; wait for a click to select the pie + } else { + // there was a move, handle the selected button/pie + HandlePieMenuMouseSelection(); + } + } + // // Menu (F10) button // @@ -1797,7 +1850,7 @@ global void UIHandleButtonUp(unsigned button) if (Selected[0]->Player == ThisPlayer) { char buf[64]; if (Selected[0]->Player->UnitTypesCount[Selected[0]->Type->Slot] > 1) { - sprintf(buf, "You have ~<%d~> %ss", + sprintf(buf, "You have ~<%d~> %ss", Selected[0]->Player->UnitTypesCount[Selected[0]->Type->Slot], Selected[0]->Type->Name); } else { @@ -1817,4 +1870,118 @@ global void UIHandleButtonUp(unsigned button) } } +/** +** Get pie menu under the cursor +*/ +local int GetPieUnderCursor(void) +{ + int i; + int x; + int y; + + x = CursorX - (CursorStartX - ICON_SIZE_X / 2); + y = CursorY - (CursorStartY - ICON_SIZE_Y / 2); + for (i = 0; i < 8; ++i) { + if (x > TheUI.PieX[i] && x < TheUI.PieX[i] + ICON_SIZE_X && + y > TheUI.PieY[i] && y < TheUI.PieY[i] + ICON_SIZE_Y) { + return i; + } + } + return -1; // no pie under cursor +} + +/** +** Draw Pie Menu +*/ +global void DrawPieMenu(void) +{ + int i; + const ButtonAction* buttons; + Viewport* vp; + Player* player; + char buf[2] = "?"; + + if(CursorState != CursorStatePieMenu) + return; + + if (!(buttons = CurrentButtons)) { // no buttons + CursorState = CursorStatePoint; + return; + } + + vp = TheUI.SelectedViewport; + PushClipping(); + SetClipping(vp->X, vp->Y, vp->EndX, vp->EndY); + + // Draw background + if (TheUI.PieMenuBackground.Graphic) { + VideoDrawClip(TheUI.PieMenuBackground.Graphic, 0, + CursorStartX - TheUI.PieMenuBackground.Graphic->Width / 2, + CursorStartY - TheUI.PieMenuBackground.Graphic->Height / 2); + } + player = Selected[0]->Player; + + for (i = 0; i < TheUI.NumButtonButtons && i < 8; ++i) { + if (buttons[i].Pos != -1) { + int x; + int y; + + x = CursorStartX - ICON_SIZE_X / 2 + TheUI.PieX[i]; + y = CursorStartY - ICON_SIZE_Y / 2 + TheUI.PieY[i]; + // Draw icon + DrawIcon(player, buttons[i].Icon.Icon, x, y); + + // Tutorial show command key in icons + if (ShowCommandKey) { + char* text; + + if (CurrentButtons[i].Key == 27) { + text = "ESC"; + } else { + buf[0] = toupper(CurrentButtons[i].Key); + text = buf; + } + VideoDrawText(x + 4, y + 4, GameFont, text); + } + } + } + + PopClipping(); + + i = GetPieUnderCursor(); + if (i != -1 && KeyState != KeyStateInput && buttons[i].Pos!=-1) { + UpdateStatusLineForButton(&buttons[i]); + } +} + +/** +** Handle pie menu mouse selection +*/ +local void HandlePieMenuMouseSelection(void) +{ + int pie; + + if (!CurrentButtons) { // no buttons + return; + } + + pie = GetPieUnderCursor(); + if (pie != -1) { + if (CurrentButtons[pie].Action == ButtonButton) { + // there is a submenu => stay in piemenu mode + // and recenter the piemenu around the cursor + CursorStartX = CursorX; + CursorStartY = CursorY; + } else { + CursorState = CursorStatePoint; + } + DoButtonButtonClicked(pie); + } else { + CursorState = CursorStatePoint; + } + + return; +} + + //@} diff --git a/src/ui/script_ui.cpp b/src/ui/script_ui.cpp index fbfa84e42..17d91a572 100644 --- a/src/ui/script_ui.cpp +++ b/src/ui/script_ui.cpp @@ -1574,6 +1574,57 @@ local int CclDefineUI(lua_State* l) LuaError(l, "Unsupported tag: %s" _C_ value); } } + } else if (!strcmp(value, "piemenu")) { + k = 0; + if (!lua_istable(l, j + 1)) { + lua_pushstring(l, "incorrect argument"); + lua_error(l); + } + subargs = luaL_getn(l, j + 1); + for (k = 0; k < subargs; ++k) { + lua_rawgeti(l, j + 1, k + 1); + value = LuaToString(l, -1); + lua_pop(l, 1); + ++k; + if (!strcmp(value, "file")) { + lua_rawgeti(l, j + 1, k + 1); + ui->PieMenuBackground.File = strdup(LuaToString(l, -1)); + lua_pop(l, 1); + } else if (!strcmp(value, "radius")) { + // Position of the pies in a piemenu + int coeffX[] = { 0, 193, 256, 193, 0, -193, -256, -193}; + int coeffY[] = { -256, -193, 0, 193, 256, 193, 0, -193}; + int pie; + int radius; + + lua_rawgeti(l, j + 1, k + 1); + radius = LuaToNumber(l, -1); + lua_pop(l, 1); + for (pie = 0; pie < 8; ++pie) { + ui->PieX[pie]= (coeffX[pie] * radius) >> 8; + ui->PieY[pie]= (coeffY[pie] * radius) >> 8; + } + } else if (!strcmp(value, "mouse-button")) { + const char *button; + + lua_rawgeti(l, j + 1, k + 1); + button = LuaToString(l, -1); + if (!strcmp(button, "right")) { + ui->PieMouseButton = RightButton; + } else if (!strcmp(button, "middle")) { + ui->PieMouseButton = MiddleButton; + } else if (!strcmp(button, "left")) { + ui->PieMouseButton = LeftButton; + } else { + ui->PieMouseButton = NoButton; + } + lua_pop(l, 1); + } else { + lua_pushfstring(l, "Unsupported tag: %s", value); + lua_error(l); + } + } + lua_pop(l, 1); } else if (!strcmp(value, "map-area")) { int w; int h; diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index 6ecb8168a..36c78572c 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -99,6 +99,7 @@ local void CleanUIGraphics(UI* ui) VideoSafeFree(ui->MenuPanel.Graphic); VideoSafeFree(ui->MinimapPanel.Graphic); VideoSafeFree(ui->StatusLine.Graphic); + VideoSafeFree(ui->PieMenuBackground.Graphic); menupanel = ui->MenuPanels; while (menupanel) { @@ -238,6 +239,10 @@ global void LoadUserInterface(void) if (TheUI.ButtonPanel.File) { TheUI.ButtonPanel.Graphic = LoadGraphic(TheUI.ButtonPanel.File); } + if (TheUI.PieMenuBackground.File) { + TheUI.PieMenuBackground.Graphic = + LoadGraphic(TheUI.PieMenuBackground.File); + } if (TheUI.MenuPanel.File) { TheUI.MenuPanel.Graphic = LoadGraphic(TheUI.MenuPanel.File); } @@ -362,6 +367,9 @@ global void CleanUI(UI* ui) // Menu Button free(ui->MenuPanel.File); + // Pie Menu + free(ui->PieMenuBackground.File); + // Minimap free(ui->MinimapPanel.File);