From 3945a4a34b562a7cf71a2c7231f296d7eaf5374f Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 25 Jul 2014 14:33:15 -0600 Subject: [PATCH] Improved three-key support. Default theme should be completely usable with three-key cabinets if ThreeKeyNavigation pref is true. Added ThreeKeyNavigation pref. ScreenOptions:NavigationMode metric now uses that pref to decide between normal and toggle modes. Added GoToFirstOnStart to OptionRowHandlerLua so an option row can control that behavior. Rewrote input for default's ScreenSelectProfile to use an input callback to take advantage of menu button mapping. Fixed inconsistent indentation in ORHL documentation. --- .../Examples/OptionRowHandlerLua.lua | 39 ++++---- Themes/_fallback/Languages/en.ini | 5 + Themes/_fallback/Scripts/02 Other.lua | 8 ++ .../_fallback/Scripts/03 CustomSpeedMods.lua | 30 +++--- Themes/_fallback/metrics.ini | 7 +- .../ScreenSelectProfile overlay.lua | 98 +++++++++++-------- src/OptionRow.cpp | 7 +- src/OptionRow.h | 1 + src/OptionRowHandler.cpp | 11 ++- src/OptionRowHandler.h | 1 + src/PrefsManager.cpp | 1 + src/PrefsManager.h | 1 + src/RageTexture.cpp | 6 ++ src/ScreenOptions.cpp | 2 +- src/ScreenOptionsMasterPrefs.cpp | 1 + src/Sprite.h | 2 +- 16 files changed, 141 insertions(+), 79 deletions(-) diff --git a/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua b/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua index 4072233bdc..20eef74c10 100644 --- a/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua +++ b/Docs/Themerdocs/Examples/OptionRowHandlerLua.lua @@ -18,13 +18,18 @@ -- The function "FooMods" returns a table containing all the information the -- option row handler needs to build the option row. function FooMods() - return { + return { -- A string with the name of the row. This name will be localized using -- the entry in the "OptionTitles" section of the language file. - Name= "Foo", + Name= "Foo", + + -- A boolean that controls whether this row goes to the "next row" + -- element when NavigationMode for the screen is "toggle" and the + -- ArcadeOptionsNavigation preference is 1. + GoToFirstOnStart= false, -- A boolean controlling whether the choice affects all players. - OneChoiceForAllPlayers= false, + OneChoiceForAllPlayers= false, -- A boolean controlling whether SaveSelections is called after every -- change. If this is true, SaveSelections will be called every time @@ -35,12 +40,12 @@ function FooMods() -- "ShowOneInRow" shows only the choice with focus is shown. -- "ShowOneInRow" is forced if there are enough choices that they would go -- off screen. - LayoutType= "ShowAllInRow", + LayoutType= "ShowAllInRow", -- A SelectType enum value. "SelectOne" allows only one choice to be -- selected. "SelectMultiple" allows multiple to be selected. -- "SelectNone" allows none to be selected. - SelectType= "SelectMultiple", + SelectType= "SelectMultiple", -- Optional function. If non-nil, this function must return a table of -- PlayerNumbers that are allowed to use the row. @@ -56,7 +61,7 @@ function FooMods() -- A table of strings that are the names of choices. Choice names are not -- localized. - Choices= {"a", "b", "c", "d"}, + Choices= {"a", "b", "c", "d"}, -- Optional table. If non-nil, this table must contain a list of messages -- this row should listen for. If one of the messages is recieved, the @@ -68,31 +73,31 @@ function FooMods() -- on the row the player has selected. -- self is the table returned by the original function used to create the -- option row. (the table being created right now). - -- list is a table of bools, all initially false. Set them to true to + -- list is a table of bools, all initially false. Set them to true to -- indicate which options are on. -- pn is the PlayerNumber of the player the selections are for. - LoadSelections= function(self, list, pn) + LoadSelections= function(self, list, pn) Trace("FooMods:LoadSelections(" .. pn .. ")") - for i, choice in ipairs(self.Choices) do + for i, choice in ipairs(self.Choices) do -- Randomly set some to true just for an example. if math.random(0, 1) == 1 then Trace(choice .. " (" .. i .. ")" .. " set to true.") list[i]= true end - end - end, + end + end, -- SaveSelections should examine the list of what the player has selected -- and apply the appropriate modifiers to the player. -- Same args as LoadSelections. - SaveSelections= function(self, list, pn) + SaveSelections= function(self, list, pn) Trace("FooMods:SaveSelections(" .. pn .. ")") - for i, choice in ipairs(self.Choices) do - if list[i] then + for i, choice in ipairs(self.Choices) do + if list[i] then Trace(choice .. " (" .. i .. ")" .. " set to true.") end - end - end, + end + end, -- Optional function. If non-nil, this function must take 3 parameters -- (self, pn, choice), and return a bool. It is called when a player @@ -137,5 +142,5 @@ function FooMods() end return change >= 2 end - } + } end diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 0369decce2..c82211fe60 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -489,6 +489,7 @@ Test Input=Test the responsiveness of connected joysticks. Test Lights=Test the responsiveness of connected lights. TextureColorDepth=Choose the color depth of textures. 32 bit textures use more memory, but look nicer. Theme=Choose from this list of installed theme packs. +ThreeKeyNavigation=&oq;Five Key&oq;: L/R/D/U menu buttons and Start. &oq;Three Key&oq;: L/R menu buttons and Start. TimingWindowScale=Increase this value to cause your steps to be judged more strictly (i.e. shrink the timing window). Toggle Song=Toggle Song Turn=Turn @@ -663,6 +664,7 @@ Expand 2->3=Expand 2->3 Expand 2x=Expand 2x Expand 3->4=Expand 3->4 Fast=Fast +Five Key Menu=Five Key Flat=Flat Flip=Flip Floored=Floored @@ -702,6 +704,7 @@ Mirror=Mirror N/A=N/A Native Language=Native Language Never=Never +NextRow=&nextrow; New=New NewEdit=-New Edit- No=No @@ -784,6 +787,7 @@ Swap Sides=Swap Sides Sync Machine=Sync Machine Sync Song=Sync Song Sync Tempo=Sync Tempo +Three Key Menu=Three Key Tiny=Tiny Tipsy=Tipsy Title Only=Title Only @@ -1119,6 +1123,7 @@ Test Input=Test Input Test Lights=Test Lights TextureColorDepth=Texture Color Theme=Theme +ThreeKeyNavigation=Menu Navigation TimingWindowScale=Judge Difficulty Transform=Transform Transfer Edits to USB=Transfer Edits to USB diff --git a/Themes/_fallback/Scripts/02 Other.lua b/Themes/_fallback/Scripts/02 Other.lua index 21044760a0..16e263388c 100644 --- a/Themes/_fallback/Scripts/02 Other.lua +++ b/Themes/_fallback/Scripts/02 Other.lua @@ -133,6 +133,14 @@ function Song:GetStageCost() return self:IsMarathon() and 3 or self:IsLong() and 2 or 1 end +function OptionsNavigationMode() + if PREFSMAN:GetPreference("ThreeKeyNavigation") then + return "toggle" + else + return "normal" + end +end + -- (c) 2005 Chris Danford -- All rights reserved. -- diff --git a/Themes/_fallback/Scripts/03 CustomSpeedMods.lua b/Themes/_fallback/Scripts/03 CustomSpeedMods.lua index ffa7ee2eb2..707595f7aa 100644 --- a/Themes/_fallback/Scripts/03 CustomSpeedMods.lua +++ b/Themes/_fallback/Scripts/03 CustomSpeedMods.lua @@ -439,21 +439,23 @@ function ArbitrarySpeedMods() NumPlayers= 0 -- for ease when adjusting for the status elements. } for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do - local poptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Preferred") - local speed= nil - local mode= nil - if poptions:MaxScrollBPM() > 0 then - mode= "m" - speed= math.round(poptions:MaxScrollBPM()) - elseif poptions:TimeSpacing() > 0 then - mode= "C" - speed= math.round(poptions:ScrollBPM()) - else - mode= "x" - speed= math.round(poptions:ScrollSpeed() * 100) + if GAMESTATE:IsHumanPlayer(pn) then + local poptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Preferred") + local speed= nil + local mode= nil + if poptions:MaxScrollBPM() > 0 then + mode= "m" + speed= math.round(poptions:MaxScrollBPM()) + elseif poptions:TimeSpacing() > 0 then + mode= "C" + speed= math.round(poptions:ScrollBPM()) + else + mode= "x" + speed= math.round(poptions:ScrollSpeed() * 100) + end + ret.CurValues[pn]= {mode= mode, speed= speed} + ret.NumPlayers= ret.NumPlayers + 1 end - ret.CurValues[pn]= {mode= mode, speed= speed} - ret.NumPlayers= ret.NumPlayers + 1 end ret:GenChoices() return ret diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index cbee4808d9..b99685446d 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -2218,7 +2218,7 @@ ScreenBeginCommand= [ScreenOptions] Fallback="ScreenWithMenuElements" -NavigationMode="normal" +NavigationMode=OptionsNavigationMode() InputMode="individual" ForceAllPlayers=false # @@ -3002,12 +3002,13 @@ Line14="conf,ShowSongOptions" Fallback="ScreenOptionsServiceChild" NextScreen="ScreenOptionsService" PrevScreen="ScreenOptionsService" -LineNames="1,2,3,4,5" +LineNames="1,2,3,4,5,6" Line1="conf,AutoMapOnJoyChange" Line2="conf,OnlyDedicatedMenuButtons" Line3="conf,DelayedBack" Line4="conf,ArcadeOptionsNavigation" -Line5="conf,MusicWheelSwitchSpeed" +Line5="conf,ThreeKeyNavigation" +Line6="conf,MusicWheelSwitchSpeed" [ScreenOptionsArcade] Fallback="ScreenOptionsServiceChild" diff --git a/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua b/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua index deb078f929..790c54b52e 100644 --- a/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua +++ b/Themes/default/BGAnimations/ScreenSelectProfile overlay.lua @@ -193,53 +193,67 @@ function UpdateInternal3(self, Player) end; end; +-- Will be set to the main ActorFrame for the screen in its OnCommand. +local main_frame= false + +local function input(event) + if event.type == "InputEventType_Release" then return end + local pn= event.PlayerNumber + local code= event.GameButton + if not pn or not code then return end + local input_functions= { + Start= function() + MESSAGEMAN:Broadcast("StartButton") + if not GAMESTATE:IsHumanPlayer(pn) then + SCREENMAN:GetTopScreen():SetProfileIndex(pn, -1) + else + SCREENMAN:GetTopScreen():Finish() + end + end, + Back= function() + if GAMESTATE:GetNumPlayersEnabled()==0 then + SCREENMAN:GetTopScreen():Cancel() + else + MESSAGEMAN:Broadcast("BackButton") + SCREENMAN:GetTopScreen():SetProfileIndex(pn, -2) + end + end, + MenuUp= function() + if GAMESTATE:IsHumanPlayer(pn) then + local ind = SCREENMAN:GetTopScreen():GetProfileIndex(pn) + if ind > 1 then + if SCREENMAN:GetTopScreen():SetProfileIndex(pn, ind - 1) then + MESSAGEMAN:Broadcast("DirectionButton") + main_frame:queuecommand('UpdateInternal2') + end + end + end + end, + MenuDown= function() + if GAMESTATE:IsHumanPlayer(pn) then + local ind = SCREENMAN:GetTopScreen():GetProfileIndex(pn) + if ind > 0 then + if SCREENMAN:GetTopScreen():SetProfileIndex(pn, ind + 1) then + MESSAGEMAN:Broadcast("DirectionButton") + main_frame:queuecommand('UpdateInternal2') + end + end + end + end + } + input_functions.MenuLeft= input_functions.MenuUp + input_functions.MenuRight= input_functions.MenuDown + if input_functions[code] then + input_functions[code]() + end +end + local t = Def.ActorFrame { StorageDevicesChangedMessageCommand=function(self, params) self:queuecommand('UpdateInternal2'); end; - CodeMessageCommand = function(self, params) - if params.Name == 'Start' or params.Name == 'Center' then - MESSAGEMAN:Broadcast("StartButton"); - if not GAMESTATE:IsHumanPlayer(params.PlayerNumber) then - SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, -1); - else - SCREENMAN:GetTopScreen():Finish(); - end; - end; - if params.Name == 'Up' or params.Name == 'Up2' or params.Name == 'DownLeft' then - if GAMESTATE:IsHumanPlayer(params.PlayerNumber) then - local ind = SCREENMAN:GetTopScreen():GetProfileIndex(params.PlayerNumber); - if ind > 1 then - if SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, ind - 1 ) then - MESSAGEMAN:Broadcast("DirectionButton"); - self:queuecommand('UpdateInternal2'); - end; - end; - end; - end; - if params.Name == 'Down' or params.Name == 'Down2' or params.Name == 'DownRight' then - if GAMESTATE:IsHumanPlayer(params.PlayerNumber) then - local ind = SCREENMAN:GetTopScreen():GetProfileIndex(params.PlayerNumber); - if ind > 0 then - if SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, ind + 1 ) then - MESSAGEMAN:Broadcast("DirectionButton"); - self:queuecommand('UpdateInternal2'); - end; - end; - end; - end; - if params.Name == 'Back' then - if GAMESTATE:GetNumPlayersEnabled()==0 then - SCREENMAN:GetTopScreen():Cancel(); - else - MESSAGEMAN:Broadcast("BackButton"); - SCREENMAN:GetTopScreen():SetProfileIndex(params.PlayerNumber, -2); - end; - end; - end; - PlayerJoinedMessageCommand=function(self, params) self:queuecommand('UpdateInternal2'); end; @@ -249,6 +263,8 @@ local t = Def.ActorFrame { end; OnCommand=function(self, params) + main_frame= self:GetParent() + SCREENMAN:GetTopScreen():AddInputCallback(input) self:queuecommand('UpdateInternal2'); end; diff --git a/src/OptionRow.cpp b/src/OptionRow.cpp index 60f7fa1e66..e5b11340f1 100644 --- a/src/OptionRow.cpp +++ b/src/OptionRow.cpp @@ -803,7 +803,7 @@ bool OptionRow::SetSelected( PlayerNumber pn, int iChoice, bool b ) bool OptionRow::NotifyHandlerOfSelection(PlayerNumber pn, int choice) { - bool changed= m_pHand->NotifyOfSelection(pn, choice); + bool changed= m_pHand->NotifyOfSelection(pn, choice - m_bFirstItemGoesDown); if(changed) { ChoicesChanged(m_RowType, false); @@ -820,6 +820,11 @@ bool OptionRow::NotifyHandlerOfSelection(PlayerNumber pn, int choice) return changed; } +bool OptionRow::GoToFirstOnStart() +{ + return m_pHand->GoToFirstOnStart(); +} + void OptionRow::SetExitText( RString sExitText ) { BitmapText *bt = m_textItems.back(); diff --git a/src/OptionRow.h b/src/OptionRow.h index 7a2c825bca..2992d97f5b 100644 --- a/src/OptionRow.h +++ b/src/OptionRow.h @@ -112,6 +112,7 @@ public: // ScreenOptions calls positions m_FrameDestination, then m_Frame tween to that same TweenState. unsigned GetTextItemsSize() const { return m_textItems.size(); } bool GetFirstItemGoesDown() const { return m_bFirstItemGoesDown; } + bool GoToFirstOnStart(); RString GetThemedItemText( int iChoice ) const; diff --git a/src/OptionRowHandler.cpp b/src/OptionRowHandler.cpp index 9d2893e2b4..be167fbc2a 100644 --- a/src/OptionRowHandler.cpp +++ b/src/OptionRowHandler.cpp @@ -833,8 +833,9 @@ public: LuaReference m_EnabledForPlayersFunc; bool m_TableIsSane; + bool m_GoToFirstOnStart; - OptionRowHandlerLua(): m_TableIsSane(false) + OptionRowHandlerLua(): m_TableIsSane(false), m_GoToFirstOnStart(false) { m_pLuaTable = new LuaReference; Init(); } virtual ~OptionRowHandlerLua() { delete m_pLuaTable; } void Init() @@ -1030,6 +1031,10 @@ public: m_Def.m_sName = pStr; lua_pop( L, 1 ); + lua_getfield(L, -1, "GoToFirstOnStart"); + m_GoToFirstOnStart= lua_toboolean(L, -1); + lua_pop(L, 1); + lua_getfield(L, -1, "OneChoiceForAllPlayers"); m_Def.m_bOneChoiceForAllPlayers = lua_toboolean( L, -1 ); lua_pop( L, 1 ); @@ -1253,6 +1258,10 @@ public: LUA->Release(L); return changed; } + virtual bool GoToFirstOnStart() + { + return m_GoToFirstOnStart; + } }; class OptionRowHandlerConfig : public OptionRowHandler diff --git a/src/OptionRowHandler.h b/src/OptionRowHandler.h index f041e8768d..28cbc8b0dd 100644 --- a/src/OptionRowHandler.h +++ b/src/OptionRowHandler.h @@ -180,6 +180,7 @@ public: virtual RString GetScreen( int /* iChoice */ ) const { return RString(); } // Exists so that a lua function can act on the selection. Returns true if the choices should be reloaded. virtual bool NotifyOfSelection(PlayerNumber pn, int choice) { return false; } + virtual bool GoToFirstOnStart() { return true; } }; /** @brief Utilities for the OptionRowHandlers. */ diff --git a/src/PrefsManager.cpp b/src/PrefsManager.cpp index 1a967e2745..27522d3628 100644 --- a/src/PrefsManager.cpp +++ b/src/PrefsManager.cpp @@ -199,6 +199,7 @@ PrefsManager::PrefsManager() : m_bShowCaution ( "ShowCaution", true ), m_bShowNativeLanguage ( "ShowNativeLanguage", true ), m_iArcadeOptionsNavigation ( "ArcadeOptionsNavigation", 0 ), + m_ThreeKeyNavigation("ThreeKeyNavigation", false), m_MusicWheelUsesSections ( "MusicWheelUsesSections", MusicWheelUsesSections_ALWAYS ), m_iMusicWheelSwitchSpeed ( "MusicWheelSwitchSpeed", 15 ), m_AllowW1 ( "AllowW1", ALLOW_W1_EVERYWHERE ), diff --git a/src/PrefsManager.h b/src/PrefsManager.h index 6ce1acfd68..509acf8d8d 100644 --- a/src/PrefsManager.h +++ b/src/PrefsManager.h @@ -191,6 +191,7 @@ public: Preference m_bShowCaution; Preference m_bShowNativeLanguage; Preference m_iArcadeOptionsNavigation; + Preference m_ThreeKeyNavigation; Preference m_MusicWheelUsesSections; Preference m_iMusicWheelSwitchSpeed; Preference m_AllowW1; // this should almost always be on, given use cases. -aj diff --git a/src/RageTexture.cpp b/src/RageTexture.cpp index 0d9e17fd90..944f54c180 100644 --- a/src/RageTexture.cpp +++ b/src/RageTexture.cpp @@ -79,6 +79,11 @@ public: lua_pushnumber( L, pRect->bottom ); return 4; } + static int GetNumFrames(T* p, lua_State* L) + { + lua_pushnumber(L, p->GetNumFrames()); + return 1; + } LunaRageTexture() { @@ -86,6 +91,7 @@ public: ADD_METHOD( loop ); ADD_METHOD( rate ); ADD_METHOD( GetTextureCoordRect ); + ADD_METHOD( GetNumFrames ); } }; diff --git a/src/ScreenOptions.cpp b/src/ScreenOptions.cpp index ecb23a5d5a..c91c44d520 100644 --- a/src/ScreenOptions.cpp +++ b/src/ScreenOptions.cpp @@ -916,7 +916,7 @@ void ScreenOptions::ProcessMenuStart( const InputEventPlus &input ) msg.SetParam( "Selected", bSelected ); MESSAGEMAN->Broadcast( msg ); - if( row.GetFirstItemGoesDown() ) + if(row.GetFirstItemGoesDown() && row.GoToFirstOnStart()) { // move to the first choice in the row ChangeValueInRowRelative( m_iCurrentRow[pn], pn, -row.GetChoiceInRowWithFocus(pn), input.type != IET_FIRST_PRESS ); diff --git a/src/ScreenOptionsMasterPrefs.cpp b/src/ScreenOptionsMasterPrefs.cpp index 942540c69e..75ea161ea1 100644 --- a/src/ScreenOptionsMasterPrefs.cpp +++ b/src/ScreenOptionsMasterPrefs.cpp @@ -705,6 +705,7 @@ static void InitializeConfOptions() ADD( ConfOption( "AutoPlay", MovePref, "Off","On","CPU-Controlled" ) ); ADD( ConfOption( "DelayedBack", MovePref, "Instant","Hold" ) ); ADD( ConfOption( "ArcadeOptionsNavigation", MovePref, "StepMania Style","Arcade Style" ) ); + ADD( ConfOption( "ThreeKeyNavigation", MovePref, "Five Key Menu", "Three Key Menu" ) ); ADD( ConfOption( "MusicWheelSwitchSpeed", MusicWheelSwitchSpeed, "Slow","Normal","Fast","Really Fast" ) ); // Gameplay options diff --git a/src/Sprite.h b/src/Sprite.h index 4e4ba791ac..f6f95e5fba 100644 --- a/src/Sprite.h +++ b/src/Sprite.h @@ -14,7 +14,7 @@ public: Sprite(); Sprite( const Sprite &cpy ); virtual ~Sprite(); - + // See explanation in source. static Sprite* NewBlankSprite();