mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Merge pull request #2168 from IvanSavenko/gui_handler_event_handling_refactoring
Event handling refactoring
This commit is contained in:
commit
2c3e8c3390
174
client/CMT.cpp
174
client/CMT.cpp
@ -14,36 +14,29 @@
|
||||
|
||||
#include "CGameInfo.h"
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
#include "mainmenu/CPrologEpilogVideo.h"
|
||||
#include "gui/CursorHandler.h"
|
||||
#include "eventsSDL/InputHandler.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CVideoHandler.h"
|
||||
#include "CMusicHandler.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/NotificationHandler.h"
|
||||
#include "ClientCommandManager.h"
|
||||
#include "windows/CMessage.h"
|
||||
#include "render/IScreenHandler.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/CConsoleHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <vstd/StringUtils.h>
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_hints.h>
|
||||
#include <SDL_main.h>
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
#include <SDL_syswm.h>
|
||||
#endif
|
||||
#include <SDL_main.h>
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "../lib/CAndroidVMHelper.h"
|
||||
@ -60,9 +53,6 @@ namespace bfs = boost::filesystem;
|
||||
|
||||
extern boost::thread_specific_ptr<bool> inGuiThread;
|
||||
|
||||
std::queue<SDL_Event> SDLEventsQueue;
|
||||
boost::mutex eventsM;
|
||||
|
||||
static po::variables_map vm;
|
||||
|
||||
#ifndef VCMI_IOS
|
||||
@ -325,19 +315,6 @@ int main(int argc, char * argv[])
|
||||
logGlobal->info("Initializing screen and sound handling: %d ms", pomtime.getDiff());
|
||||
}
|
||||
|
||||
#ifdef VCMI_MAC
|
||||
// Ctrl+click should be treated as a right click on Mac OS X
|
||||
SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1");
|
||||
#endif
|
||||
|
||||
#ifdef SDL_HINT_MOUSE_TOUCH_EVENTS
|
||||
if(GH.isPointerRelativeMode)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef VCMI_NO_THREADED_LOAD
|
||||
//we can properly play intro only in the main thread, so we have to move loading to the separate thread
|
||||
boost::thread loading(init);
|
||||
@ -469,147 +446,18 @@ void playIntro()
|
||||
}
|
||||
}
|
||||
|
||||
static void handleEvent(SDL_Event & ev)
|
||||
{
|
||||
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
|
||||
{
|
||||
#ifdef VCMI_ANDROID
|
||||
handleQuit(false);
|
||||
#else
|
||||
handleQuit();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#ifdef VCMI_ANDROID
|
||||
else if (ev.type == SDL_KEYDOWN && ev.key.keysym.scancode == SDL_SCANCODE_AC_BACK)
|
||||
{
|
||||
handleQuit(true);
|
||||
}
|
||||
#endif
|
||||
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
|
||||
{
|
||||
Settings full = settings.write["video"]["fullscreen"];
|
||||
full->Bool() = !full->Bool();
|
||||
return;
|
||||
}
|
||||
else if(ev.type == SDL_USEREVENT)
|
||||
{
|
||||
switch(static_cast<EUserEvent>(ev.user.code))
|
||||
{
|
||||
case EUserEvent::FORCE_QUIT:
|
||||
{
|
||||
handleQuit(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case EUserEvent::RETURN_TO_MAIN_MENU:
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("main");
|
||||
}
|
||||
break;
|
||||
case EUserEvent::RESTART_GAME:
|
||||
{
|
||||
CSH->sendRestartGame();
|
||||
}
|
||||
break;
|
||||
case EUserEvent::CAMPAIGN_START_SCENARIO:
|
||||
{
|
||||
CSH->campaignServerRestartLock.set(true);
|
||||
CSH->endGameplay();
|
||||
auto ourCampaign = std::shared_ptr<CCampaignState>(reinterpret_cast<CCampaignState *>(ev.user.data1));
|
||||
auto & epilogue = ourCampaign->camp->scenarios[ourCampaign->mapsConquered.back()].epilog;
|
||||
auto finisher = [=]()
|
||||
{
|
||||
if(ourCampaign->mapsRemaining.size())
|
||||
{
|
||||
GH.windows().pushWindow(CMM);
|
||||
GH.windows().pushWindow(CMM->menu);
|
||||
CMM->openCampaignLobby(ourCampaign);
|
||||
}
|
||||
};
|
||||
if(epilogue.hasPrologEpilog)
|
||||
{
|
||||
GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher);
|
||||
}
|
||||
else
|
||||
{
|
||||
CSH->campaignServerRestartLock.waitUntil(false);
|
||||
finisher();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EUserEvent::RETURN_TO_MENU_LOAD:
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("load");
|
||||
break;
|
||||
case EUserEvent::FULLSCREEN_TOGGLED:
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.onScreenResize();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logGlobal->error("Unknown user event. Code %d", ev.user.code);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if(ev.type == SDL_WINDOWEVENT)
|
||||
{
|
||||
switch (ev.window.event) {
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
#ifndef VCMI_IOS
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.onScreenResize();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(ev.type == SDL_SYSWMEVENT)
|
||||
{
|
||||
if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
|
||||
{
|
||||
NotificationHandler::handleSdlEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
//preprocessing
|
||||
if(ev.type == SDL_MOUSEMOTION)
|
||||
{
|
||||
CCS->curh->cursorMove(ev.motion.x, ev.motion.y);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(eventsM);
|
||||
SDLEventsQueue.push(ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void mainLoop()
|
||||
{
|
||||
SettingsListener resChanged = settings.listen["video"]["resolution"];
|
||||
SettingsListener fsChanged = settings.listen["video"]["fullscreen"];
|
||||
resChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
fsChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
resChanged([](const JsonNode &newState){ GH.pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
fsChanged([](const JsonNode &newState){ GH.pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
|
||||
inGuiThread.reset(new bool(true));
|
||||
|
||||
while(1) //main SDL events loop
|
||||
{
|
||||
SDL_Event ev;
|
||||
|
||||
while(1 == SDL_PollEvent(&ev))
|
||||
{
|
||||
handleEvent(ev);
|
||||
}
|
||||
|
||||
GH.input().fetchEvents();
|
||||
CSH->applyPacksOnLobbyScreen();
|
||||
GH.renderFrame();
|
||||
}
|
||||
@ -667,15 +515,7 @@ void handleQuit(bool ask)
|
||||
if(CSH->client && LOCPLINT && ask)
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], [](){
|
||||
// Workaround for assertion failure on exit:
|
||||
// handleQuit() is alway called during SDL event processing
|
||||
// during which, eventsM is kept locked
|
||||
// this leads to assertion failure if boost::mutex is in locked state
|
||||
eventsM.unlock();
|
||||
|
||||
quitApplication();
|
||||
}, nullptr);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], quitApplication, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -27,12 +27,21 @@ set(client_SRCS
|
||||
battle/BattleWindow.cpp
|
||||
battle/CreatureAnimation.cpp
|
||||
|
||||
eventsSDL/NotificationHandler.cpp
|
||||
eventsSDL/InputHandler.cpp
|
||||
eventsSDL/UserEventHandler.cpp
|
||||
eventsSDL/InputSourceKeyboard.cpp
|
||||
eventsSDL/InputSourceMouse.cpp
|
||||
eventsSDL/InputSourceText.cpp
|
||||
eventsSDL/InputSourceTouch.cpp
|
||||
|
||||
gui/CGuiHandler.cpp
|
||||
gui/CIntObject.cpp
|
||||
gui/CursorHandler.cpp
|
||||
gui/EventDispatcher.cpp
|
||||
gui/EventsReceiver.cpp
|
||||
gui/InterfaceObjectConfigurable.cpp
|
||||
gui/FramerateManager.cpp
|
||||
gui/NotificationHandler.cpp
|
||||
gui/ShortcutHandler.cpp
|
||||
gui/WindowHandler.cpp
|
||||
|
||||
@ -160,13 +169,22 @@ set(client_HEADERS
|
||||
battle/BattleWindow.h
|
||||
battle/CreatureAnimation.h
|
||||
|
||||
eventsSDL/NotificationHandler.h
|
||||
eventsSDL/InputHandler.h
|
||||
eventsSDL/UserEventHandler.h
|
||||
eventsSDL/InputSourceKeyboard.h
|
||||
eventsSDL/InputSourceMouse.h
|
||||
eventsSDL/InputSourceText.h
|
||||
eventsSDL/InputSourceTouch.h
|
||||
|
||||
gui/CGuiHandler.h
|
||||
gui/CIntObject.h
|
||||
gui/CursorHandler.h
|
||||
gui/EventDispatcher.h
|
||||
gui/EventsReceiver.h
|
||||
gui/InterfaceObjectConfigurable.h
|
||||
gui/FramerateManager.h
|
||||
gui/MouseButton.h
|
||||
gui/NotificationHandler.h
|
||||
gui/Shortcut.h
|
||||
gui/ShortcutHandler.h
|
||||
gui/TextAlignment.h
|
||||
@ -333,7 +351,7 @@ if(WIN32)
|
||||
endif()
|
||||
target_compile_definitions(vcmiclient PRIVATE WINDOWS_IGNORE_PACKING_MISMATCH)
|
||||
|
||||
# TODO: very hacky, find proper solution to copy AI dlls into bin dir
|
||||
# TODO: very hacky, find proper solution to copy AI dlls into bin dir
|
||||
if(MSVC)
|
||||
add_custom_command(TARGET vcmiclient POST_BUILD
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:vcmiclient>"
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "battle/BattleWindow.h"
|
||||
#include "../CCallback.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "eventsSDL/InputHandler.h"
|
||||
#include "gui/CursorHandler.h"
|
||||
#include "windows/CKingdomInterface.h"
|
||||
#include "CGameInfo.h"
|
||||
@ -74,11 +75,9 @@
|
||||
#include "CServerHandler.h"
|
||||
// FIXME: only needed for CGameState::mutex
|
||||
#include "../lib/CGameState.h"
|
||||
#include "gui/NotificationHandler.h"
|
||||
#include "eventsSDL/NotificationHandler.h"
|
||||
#include "adventureMap/CInGameConsole.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
|
||||
// The macro below is used to mark functions that are called by client when game state changes.
|
||||
// They all assume that CPlayerInterface::pim mutex is locked.
|
||||
#define EVENT_HANDLER_CALLED_BY_CLIENT
|
||||
@ -96,8 +95,6 @@
|
||||
return; \
|
||||
RETURN_IF_QUICK_COMBAT
|
||||
|
||||
extern std::queue<SDL_Event> SDLEventsQueue;
|
||||
extern boost::mutex eventsM;
|
||||
boost::recursive_mutex * CPlayerInterface::pim = new boost::recursive_mutex;
|
||||
|
||||
CPlayerInterface * LOCPLINT;
|
||||
@ -206,7 +203,7 @@ void CPlayerInterface::performAutosave()
|
||||
}
|
||||
else if(frequency > 0 && cb->getDate() % frequency == 0)
|
||||
{
|
||||
LOCPLINT->cb->save("Saves/" + prefix + "Autosave_" + std::to_string(autosaveCount++ + 1));
|
||||
cb->save("Saves/" + prefix + "Autosave_" + std::to_string(autosaveCount++ + 1));
|
||||
autosaveCount %= 5;
|
||||
}
|
||||
}
|
||||
@ -215,8 +212,6 @@ void CPlayerInterface::yourTurn()
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(eventsM); //block handling events until interface is ready
|
||||
|
||||
LOCPLINT = this;
|
||||
GH.curInt = this;
|
||||
|
||||
@ -372,22 +367,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
|
||||
|
||||
//check if user cancelled movement
|
||||
{
|
||||
boost::unique_lock<boost::mutex> un(eventsM);
|
||||
while(!SDLEventsQueue.empty())
|
||||
{
|
||||
SDL_Event ev = SDLEventsQueue.front();
|
||||
SDLEventsQueue.pop();
|
||||
switch(ev.type)
|
||||
{
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
stillMoveHero.setn(STOP_MOVE);
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
if (ev.key.keysym.sym < SDLK_F1 || ev.key.keysym.sym > SDLK_F15)
|
||||
stillMoveHero.setn(STOP_MOVE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (GH.input().ignoreEventsUntilInput())
|
||||
stillMoveHero.setn(STOP_MOVE);
|
||||
}
|
||||
|
||||
if (stillMoveHero.get() == WAITING_MOVE)
|
||||
@ -1478,7 +1459,6 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
|
||||
if(CSH->howManyPlayerInterfaces() > 1 && LOCPLINT != this && LOCPLINT->makingTurn == false)
|
||||
{
|
||||
//one of our players who isn't last in order got attacked not by our another player (happens for example in hotseat mode)
|
||||
boost::unique_lock<boost::mutex> lock(eventsM); //TODO: copied from yourTurn, no idea if it's needed
|
||||
LOCPLINT = this;
|
||||
GH.curInt = this;
|
||||
adventureInt->onCurrentPlayerChanged(playerID);
|
||||
@ -1513,7 +1493,6 @@ void CPlayerInterface::update()
|
||||
assert(adventureInt);
|
||||
|
||||
// Handles mouse and key input
|
||||
GH.updateTime();
|
||||
GH.handleEvents();
|
||||
GH.windows().simpleRedraw();
|
||||
}
|
||||
@ -1872,15 +1851,11 @@ bool CPlayerInterface::capturedAllEvents()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needToLockAdventureMap = adventureInt && adventureInt->active && CGI->mh->hasOngoingAnimations();
|
||||
bool needToLockAdventureMap = adventureInt && adventureInt->isActive() && CGI->mh->hasOngoingAnimations();
|
||||
|
||||
if (ignoreEvents || needToLockAdventureMap || isAutoFightOn)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> un(eventsM);
|
||||
while(!SDLEventsQueue.empty())
|
||||
{
|
||||
SDLEventsQueue.pop();
|
||||
}
|
||||
GH.input().ignoreEventsUntilInput();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,6 @@ class KeyInterested;
|
||||
class MotionInterested;
|
||||
class PlayerLocalState;
|
||||
class TimeInterested;
|
||||
class IShowable;
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
@ -843,7 +843,7 @@ void CServerHandler::threadHandleConnection()
|
||||
if(client)
|
||||
{
|
||||
state = EClientState::DISCONNECTING;
|
||||
CGuiHandler::pushUserEvent(EUserEvent::RETURN_TO_MAIN_MENU);
|
||||
GH.pushUserEvent(EUserEvent::RETURN_TO_MAIN_MENU);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -12,15 +12,13 @@
|
||||
|
||||
#include "CMT.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "eventsSDL/InputHandler.h"
|
||||
#include "gui/FramerateManager.h"
|
||||
#include "renderSDL/SDL_Extensions.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_events.h>
|
||||
|
||||
extern CGuiHandler GH; //global gui handler
|
||||
|
||||
#ifndef DISABLE_VIDEO
|
||||
|
||||
@ -31,18 +29,6 @@ extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
//reads events and returns true on key down
|
||||
static bool keyDown()
|
||||
{
|
||||
SDL_Event ev;
|
||||
while(SDL_PollEvent(&ev))
|
||||
{
|
||||
if(ev.type == SDL_KEYDOWN || ev.type == SDL_MOUSEBUTTONDOWN)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "avcodec.lib")
|
||||
#pragma comment(lib, "avutil.lib")
|
||||
@ -371,7 +357,7 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
|
||||
auto packet_duration = frame->duration;
|
||||
#endif
|
||||
double frameEndTime = (frame->pts + packet_duration) * av_q2d(format->streams[stream]->time_base);
|
||||
frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.0;
|
||||
frameTime += GH.framerate().getElapsedMilliseconds() / 1000.0;
|
||||
|
||||
if (frameTime >= frameEndTime )
|
||||
{
|
||||
@ -455,8 +441,12 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
|
||||
|
||||
while(nextFrame())
|
||||
{
|
||||
if(stopOnKey && keyDown())
|
||||
return false;
|
||||
if(stopOnKey)
|
||||
{
|
||||
GH.input().fetchEvents();
|
||||
if(GH.input().ignoreEventsUntilInput())
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Rect rect = CSDL_Ext::toSDL(pos);
|
||||
|
||||
|
@ -194,12 +194,6 @@ void ClientCommandManager::handleNotDialogCommand()
|
||||
LOCPLINT->showingDialog->setn(false);
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleGuiCommand()
|
||||
{
|
||||
for(const auto & child : GH.windows().findWindows<CIntObject>())
|
||||
printInfoAboutInterfaceObject(child.get(), 0);
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleConvertTextCommand()
|
||||
{
|
||||
logGlobal->info("Searching for available maps");
|
||||
@ -487,36 +481,6 @@ void ClientCommandManager::printCommandMessage(const std::string &commandMessage
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCommandManager::printInfoAboutInterfaceObject(const CIntObject *obj, int level)
|
||||
{
|
||||
std::stringstream sbuffer;
|
||||
sbuffer << std::string(level, '\t');
|
||||
|
||||
sbuffer << typeid(*obj).name() << " *** ";
|
||||
if (obj->active)
|
||||
{
|
||||
#define PRINT(check, text) if (obj->active & CIntObject::check) sbuffer << text
|
||||
PRINT(LCLICK, 'L');
|
||||
PRINT(RCLICK, 'R');
|
||||
PRINT(HOVER, 'H');
|
||||
PRINT(MOVE, 'M');
|
||||
PRINT(KEYBOARD, 'K');
|
||||
PRINT(TIME, 'T');
|
||||
PRINT(GENERAL, 'A');
|
||||
PRINT(WHEEL, 'W');
|
||||
PRINT(DOUBLECLICK, 'D');
|
||||
#undef PRINT
|
||||
}
|
||||
else
|
||||
sbuffer << "inactive";
|
||||
sbuffer << " at " << obj->pos.x <<"x"<< obj->pos.y;
|
||||
sbuffer << " (" << obj->pos.w <<"x"<< obj->pos.h << ")";
|
||||
printCommandMessage(sbuffer.str(), ELogLevel::INFO);
|
||||
|
||||
for(const CIntObject *child : obj->children)
|
||||
printInfoAboutInterfaceObject(child, level+1);
|
||||
}
|
||||
|
||||
void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
|
||||
{
|
||||
YourTurn yt;
|
||||
@ -569,9 +533,6 @@ void ClientCommandManager::processCommand(const std::string & message, bool call
|
||||
else if(commandName == "not dialog")
|
||||
handleNotDialogCommand();
|
||||
|
||||
else if(commandName == "gui")
|
||||
handleGuiCommand();
|
||||
|
||||
else if(message=="convert txt")
|
||||
handleConvertTextCommand();
|
||||
|
||||
|
@ -51,9 +51,6 @@ class ClientCommandManager //take mantis #2292 issue about account if thinking a
|
||||
// Set the state indicating if dialog box is active to "no"
|
||||
void handleNotDialogCommand();
|
||||
|
||||
// Displays tree view of currently present VCMI common GUI elements
|
||||
void handleGuiCommand();
|
||||
|
||||
// Dumps all game text, maps text and campaign maps text into Client log between BEGIN TEXT EXPORT and END TEXT EXPORT
|
||||
void handleConvertTextCommand();
|
||||
|
||||
@ -92,7 +89,6 @@ class ClientCommandManager //take mantis #2292 issue about account if thinking a
|
||||
|
||||
// Prints in Chat the given message
|
||||
void printCommandMessage(const std::string &commandMessage, ELogLevel::ELogLevel messageType = ELogLevel::NOT_SET);
|
||||
void printInfoAboutInterfaceObject(const CIntObject *obj, int level);
|
||||
void giveTurn(const PlayerColor &color);
|
||||
|
||||
public:
|
||||
|
@ -52,7 +52,7 @@ AdventureMapInterface::AdventureMapInterface():
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
|
||||
setMoveEventStrongInterest(true); // handle all mouse move events to prevent dead mouse move space in fullscreen mode
|
||||
|
||||
shortcuts = std::make_shared<AdventureMapShortcuts>(*this);
|
||||
|
||||
@ -179,7 +179,7 @@ void AdventureMapInterface::handleMapScrollingUpdate(uint32_t timePassed)
|
||||
Point scrollDelta = scrollDirection * scrollDistance;
|
||||
|
||||
bool cursorInScrollArea = scrollDelta != Point(0,0);
|
||||
bool scrollingActive = cursorInScrollArea && active && shortcuts->optionSidePanelActive() && !scrollingWasBlocked;
|
||||
bool scrollingActive = cursorInScrollArea && isActive() && shortcuts->optionSidePanelActive() && !scrollingWasBlocked;
|
||||
bool scrollingBlocked = GH.isKeyboardCtrlDown();
|
||||
|
||||
if (!scrollingWasActive && scrollingBlocked)
|
||||
@ -323,19 +323,19 @@ void AdventureMapInterface::setState(EAdventureState state)
|
||||
|
||||
void AdventureMapInterface::adjustActiveness()
|
||||
{
|
||||
bool widgetMustBeActive = active && shortcuts->optionSidePanelActive();
|
||||
bool mapViewMustBeActive = active && (shortcuts->optionMapViewActive());
|
||||
bool widgetMustBeActive = isActive() && shortcuts->optionSidePanelActive();
|
||||
bool mapViewMustBeActive = isActive() && (shortcuts->optionMapViewActive());
|
||||
|
||||
if (widgetMustBeActive && !widget->active)
|
||||
if (widgetMustBeActive && !widget->isActive())
|
||||
widget->activate();
|
||||
|
||||
if (!widgetMustBeActive && widget->active)
|
||||
if (!widgetMustBeActive && widget->isActive())
|
||||
widget->deactivate();
|
||||
|
||||
if (mapViewMustBeActive && !widget->getMapView()->active)
|
||||
if (mapViewMustBeActive && !widget->getMapView()->isActive())
|
||||
widget->getMapView()->activate();
|
||||
|
||||
if (!mapViewMustBeActive && widget->getMapView()->active)
|
||||
if (!mapViewMustBeActive && widget->getMapView()->isActive())
|
||||
widget->getMapView()->deactivate();
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ void CInGameConsole::textEdited(const std::string & inputtedText)
|
||||
|
||||
void CInGameConsole::startEnteringText()
|
||||
{
|
||||
if (!active)
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
if (captureAllKeys)
|
||||
|
@ -316,8 +316,7 @@ CInfoBar::CInfoBar(const Point & position): CInfoBar(Rect(position.x, position.y
|
||||
|
||||
void CInfoBar::setTimer(uint32_t msToTrigger)
|
||||
{
|
||||
if (!(active & TIME))
|
||||
addUsedEvents(TIME);
|
||||
addUsedEvents(TIME);
|
||||
timerCounter = msToTrigger;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
@ -131,7 +132,7 @@ void CMinimap::moveAdvMapSelection()
|
||||
int3 newLocation = pixelToTile(GH.getCursorPosition() - pos.topLeft());
|
||||
adventureInt->centerOnTile(newLocation);
|
||||
|
||||
if (!(adventureInt->active & GENERAL))
|
||||
if (!(adventureInt->isActive()))
|
||||
GH.windows().totalRedraw(); //redraw this as well as inactive adventure map
|
||||
else
|
||||
redraw();//redraw only this
|
||||
@ -159,7 +160,7 @@ void CMinimap::hover(bool on)
|
||||
|
||||
void CMinimap::mouseMoved(const Point & cursorPosition)
|
||||
{
|
||||
if(mouseState(MouseButton::LEFT))
|
||||
if(isMouseButtonPressed(MouseButton::LEFT))
|
||||
moveAdvMapSelection();
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
|
||||
owner(owner)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
strongInterest = true;
|
||||
setMoveEventStrongInterest(true);
|
||||
|
||||
//preparing cells and hexes
|
||||
cellBorder = IImage::createFromFile("CCELLGRD.BMP", EImageBlitMode::COLORKEY);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/IImage.h"
|
||||
@ -692,7 +693,7 @@ std::optional<uint32_t> StackQueue::getHoveredUnitIdIfAny() const
|
||||
{
|
||||
for(const auto & stackBox : stackBoxes)
|
||||
{
|
||||
if(stackBox->hovered || stackBox->mouseState(MouseButton::RIGHT))
|
||||
if(stackBox->isHovered() || stackBox->isMouseButtonPressed(MouseButton::RIGHT))
|
||||
{
|
||||
return stackBox->getBoundUnitID();
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ void BattleStacksController::initializeBattleAnimations()
|
||||
|
||||
void BattleStacksController::tickFrameBattleAnimations(uint32_t msPassed)
|
||||
{
|
||||
for (auto stack : owner.curInt->cb->battleGetAllStacks(false))
|
||||
for (auto stack : owner.curInt->cb->battleGetAllStacks(true))
|
||||
{
|
||||
if (stackAnimation.find(stack->unitId()) == stackAnimation.end()) //e.g. for summoned but not yet handled stacks
|
||||
continue;
|
||||
|
269
client/eventsSDL/InputHandler.cpp
Normal file
269
client/eventsSDL/InputHandler.cpp
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* InputHandler.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "InputHandler.h"
|
||||
|
||||
#include "NotificationHandler.h"
|
||||
#include "InputSourceMouse.h"
|
||||
#include "InputSourceKeyboard.h"
|
||||
#include "InputSourceTouch.h"
|
||||
#include "InputSourceText.h"
|
||||
#include "UserEventHandler.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/EventDispatcher.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
#include "../CMT.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CGameInfo.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_hints.h>
|
||||
|
||||
InputHandler::InputHandler()
|
||||
: mouseHandler(std::make_unique<InputSourceMouse>())
|
||||
, keyboardHandler(std::make_unique<InputSourceKeyboard>())
|
||||
, fingerHandler(std::make_unique<InputSourceTouch>())
|
||||
, textHandler(std::make_unique<InputSourceText>())
|
||||
, userHandler(std::make_unique<UserEventHandler>())
|
||||
, mouseButtonsMask(0)
|
||||
, pointerSpeedMultiplier(settings["general"]["relativePointerSpeedMultiplier"].Float())
|
||||
{
|
||||
}
|
||||
|
||||
InputHandler::~InputHandler() = default;
|
||||
|
||||
void InputHandler::handleCurrentEvent(const SDL_Event & current)
|
||||
{
|
||||
switch (current.type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
return keyboardHandler->handleEventKeyDown(current.key);
|
||||
case SDL_KEYUP:
|
||||
return keyboardHandler->handleEventKeyUp(current.key);
|
||||
case SDL_MOUSEMOTION:
|
||||
return mouseHandler->handleEventMouseMotion(current.motion);
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
return mouseHandler->handleEventMouseButtonDown(current.button);
|
||||
case SDL_MOUSEWHEEL:
|
||||
return mouseHandler->handleEventMouseWheel(current.wheel);
|
||||
case SDL_TEXTINPUT:
|
||||
return textHandler->handleEventTextInput(current.text);
|
||||
case SDL_TEXTEDITING:
|
||||
return textHandler->handleEventTextEditing(current.edit);
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
return mouseHandler->handleEventMouseButtonUp(current.button);
|
||||
case SDL_FINGERMOTION:
|
||||
return fingerHandler->handleEventFingerMotion(current.tfinger);
|
||||
case SDL_FINGERDOWN:
|
||||
return fingerHandler->handleEventFingerDown(current.tfinger);
|
||||
case SDL_FINGERUP:
|
||||
return fingerHandler->handleEventFingerUp(current.tfinger);
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler::processEvents()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(eventsMutex);
|
||||
for (auto const & currentEvent : eventsQueue)
|
||||
{
|
||||
if (currentEvent.type == SDL_MOUSEMOTION)
|
||||
{
|
||||
cursorPosition = Point(currentEvent.motion.x, currentEvent.motion.y);
|
||||
mouseButtonsMask = currentEvent.motion.state;
|
||||
}
|
||||
handleCurrentEvent(currentEvent);
|
||||
}
|
||||
eventsQueue.clear();
|
||||
}
|
||||
|
||||
bool InputHandler::ignoreEventsUntilInput()
|
||||
{
|
||||
bool inputFound = false;
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(eventsMutex);
|
||||
for (auto const & event : eventsQueue)
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_KEYDOWN:
|
||||
inputFound = true;
|
||||
}
|
||||
}
|
||||
eventsQueue.clear();
|
||||
|
||||
return inputFound;
|
||||
}
|
||||
|
||||
void InputHandler::preprocessEvent(const SDL_Event & ev)
|
||||
{
|
||||
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
|
||||
{
|
||||
#ifdef VCMI_ANDROID
|
||||
handleQuit(false);
|
||||
#else
|
||||
handleQuit();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#ifdef VCMI_ANDROID
|
||||
else if (ev.type == SDL_KEYDOWN && ev.key.keysym.scancode == SDL_SCANCODE_AC_BACK)
|
||||
{
|
||||
handleQuit(true);
|
||||
}
|
||||
#endif
|
||||
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
|
||||
{
|
||||
Settings full = settings.write["video"]["fullscreen"];
|
||||
full->Bool() = !full->Bool();
|
||||
return;
|
||||
}
|
||||
else if(ev.type == SDL_USEREVENT)
|
||||
{
|
||||
userHandler->handleUserEvent(ev.user);
|
||||
|
||||
return;
|
||||
}
|
||||
else if(ev.type == SDL_WINDOWEVENT)
|
||||
{
|
||||
switch (ev.window.event) {
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
#ifndef VCMI_IOS
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.onScreenResize();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(ev.type == SDL_SYSWMEVENT)
|
||||
{
|
||||
if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
|
||||
{
|
||||
NotificationHandler::handleSdlEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
//preprocessing
|
||||
if(ev.type == SDL_MOUSEMOTION)
|
||||
{
|
||||
if (CCS && CCS->curh)
|
||||
CCS->curh->cursorMove(ev.motion.x, ev.motion.y);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(eventsMutex);
|
||||
|
||||
if(ev.type == SDL_MOUSEMOTION && !eventsQueue.empty() && eventsQueue.back().type == SDL_MOUSEMOTION)
|
||||
{
|
||||
// In a sequence of mouse motion events, skip all but the last one.
|
||||
// This prevents freezes when every motion event takes longer to handle than interval at which
|
||||
// the events arrive (like dragging on the minimap in world view, with redraw at every event)
|
||||
// so that the events would start piling up faster than they can be processed.
|
||||
eventsQueue.back() = ev;
|
||||
return;
|
||||
}
|
||||
eventsQueue.push_back(ev);
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler::fetchEvents()
|
||||
{
|
||||
SDL_Event ev;
|
||||
|
||||
while(1 == SDL_PollEvent(&ev))
|
||||
{
|
||||
preprocessEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputHandler::isKeyboardCtrlDown() const
|
||||
{
|
||||
#ifdef VCMI_MAC
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LGUI] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RGUI];
|
||||
#else
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RCTRL];
|
||||
#endif
|
||||
}
|
||||
|
||||
bool InputHandler::isKeyboardAltDown() const
|
||||
{
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LALT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RALT];
|
||||
}
|
||||
|
||||
bool InputHandler::isKeyboardShiftDown() const
|
||||
{
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LSHIFT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RSHIFT];
|
||||
}
|
||||
|
||||
|
||||
void InputHandler::fakeMoveCursor(float dx, float dy)
|
||||
{
|
||||
int x, y, w, h;
|
||||
|
||||
SDL_Event event;
|
||||
SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
sme.state = SDL_GetMouseState(&x, &y);
|
||||
SDL_GetWindowSize(mainWindow, &w, &h);
|
||||
|
||||
sme.x = GH.getCursorPosition().x + (int)(pointerSpeedMultiplier * w * dx);
|
||||
sme.y = GH.getCursorPosition().y + (int)(pointerSpeedMultiplier * h * dy);
|
||||
|
||||
vstd::abetween(sme.x, 0, w);
|
||||
vstd::abetween(sme.y, 0, h);
|
||||
|
||||
event.motion = sme;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void InputHandler::startTextInput(const Rect & where)
|
||||
{
|
||||
textHandler->startTextInput(where);
|
||||
}
|
||||
|
||||
void InputHandler::stopTextInput()
|
||||
{
|
||||
textHandler->stopTextInput();
|
||||
}
|
||||
|
||||
bool InputHandler::isMouseButtonPressed(MouseButton button) const
|
||||
{
|
||||
static_assert(static_cast<uint32_t>(MouseButton::LEFT) == SDL_BUTTON_LEFT, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::MIDDLE) == SDL_BUTTON_MIDDLE, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::RIGHT) == SDL_BUTTON_RIGHT, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::EXTRA1) == SDL_BUTTON_X1, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::EXTRA2) == SDL_BUTTON_X2, "mismatch between VCMI and SDL enum!");
|
||||
|
||||
uint32_t index = static_cast<uint32_t>(button);
|
||||
return mouseButtonsMask & SDL_BUTTON(index);
|
||||
}
|
||||
|
||||
void InputHandler::pushUserEvent(EUserEvent usercode, void * userdata)
|
||||
{
|
||||
SDL_Event event;
|
||||
event.type = SDL_USEREVENT;
|
||||
event.user.code = static_cast<int32_t>(usercode);
|
||||
event.user.data1 = userdata;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
const Point & InputHandler::getCursorPosition() const
|
||||
{
|
||||
return cursorPosition;
|
||||
}
|
77
client/eventsSDL/InputHandler.h
Normal file
77
client/eventsSDL/InputHandler.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* InputHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../lib/Rect.h"
|
||||
|
||||
enum class EUserEvent;
|
||||
enum class MouseButton;
|
||||
union SDL_Event;
|
||||
|
||||
class InputSourceMouse;
|
||||
class InputSourceKeyboard;
|
||||
class InputSourceTouch;
|
||||
class InputSourceText;
|
||||
class UserEventHandler;
|
||||
|
||||
class InputHandler
|
||||
{
|
||||
std::vector<SDL_Event> eventsQueue;
|
||||
boost::mutex eventsMutex;
|
||||
|
||||
Point cursorPosition;
|
||||
float pointerSpeedMultiplier;
|
||||
int mouseButtonsMask;
|
||||
|
||||
void preprocessEvent(const SDL_Event & event);
|
||||
void handleCurrentEvent(const SDL_Event & current);
|
||||
|
||||
std::unique_ptr<InputSourceMouse> mouseHandler;
|
||||
std::unique_ptr<InputSourceKeyboard> keyboardHandler;
|
||||
std::unique_ptr<InputSourceTouch> fingerHandler;
|
||||
std::unique_ptr<InputSourceText> textHandler;
|
||||
std::unique_ptr<UserEventHandler> userHandler;
|
||||
|
||||
public:
|
||||
InputHandler();
|
||||
~InputHandler();
|
||||
|
||||
/// Fetches events from SDL input system and prepares them for processing
|
||||
void fetchEvents();
|
||||
/// Performs actual processing and dispatching of previously fetched events
|
||||
void processEvents();
|
||||
|
||||
/// drops all incoming events without processing them
|
||||
/// returns true if input event has been found
|
||||
bool ignoreEventsUntilInput();
|
||||
|
||||
void fakeMoveCursor(float dx, float dy);
|
||||
|
||||
/// Initiates text input in selected area, potentially creating IME popup (mobile systems only at the moment)
|
||||
void startTextInput(const Rect & where);
|
||||
|
||||
/// Ends any existing text input state
|
||||
void stopTextInput();
|
||||
|
||||
/// Returns true if selected mouse button is pressed at the moment
|
||||
bool isMouseButtonPressed(MouseButton button) const;
|
||||
|
||||
/// Generates new user event that will be processed on next frame
|
||||
void pushUserEvent(EUserEvent usercode, void * userdata);
|
||||
|
||||
/// Returns current position of cursor, in VCMI logical screen coordinates
|
||||
const Point & getCursorPosition() const;
|
||||
|
||||
/// returns true if chosen keyboard key is currently pressed down
|
||||
bool isKeyboardAltDown() const;
|
||||
bool isKeyboardCtrlDown() const;
|
||||
bool isKeyboardShiftDown() const;
|
||||
};
|
85
client/eventsSDL/InputSourceKeyboard.cpp
Normal file
85
client/eventsSDL/InputSourceKeyboard.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* InputSourceKeyboard.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "InputSourceKeyboard.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/EventDispatcher.h"
|
||||
#include "../gui/ShortcutHandler.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_hints.h>
|
||||
|
||||
InputSourceKeyboard::InputSourceKeyboard()
|
||||
{
|
||||
#ifdef VCMI_MAC
|
||||
// Ctrl+click should be treated as a right click on Mac OS X
|
||||
SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1");
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
if(key.repeat != 0)
|
||||
return; // ignore periodic event resends
|
||||
|
||||
assert(key.state == SDL_PRESSED);
|
||||
|
||||
if(key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
|
||||
{
|
||||
//TODO: we need some central place for all interface-independent hotkeys
|
||||
Settings s = settings.write["session"];
|
||||
switch(key.keysym.sym)
|
||||
{
|
||||
case SDLK_F5:
|
||||
if(settings["session"]["spectate-locked-pim"].Bool())
|
||||
CPlayerInterface::pim->unlock();
|
||||
else
|
||||
CPlayerInterface::pim->lock();
|
||||
s["spectate-locked-pim"].Bool() = !settings["session"]["spectate-locked-pim"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F6:
|
||||
s["spectate-ignore-hero"].Bool() = !settings["session"]["spectate-ignore-hero"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F7:
|
||||
s["spectate-skip-battle"].Bool() = !settings["session"]["spectate-skip-battle"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F8:
|
||||
s["spectate-skip-battle-result"].Bool() = !settings["session"]["spectate-skip-battle-result"].Bool();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto shortcutsVector = GH.shortcuts().translateKeycode(key.keysym.sym);
|
||||
|
||||
GH.events().dispatchShortcutPressed(shortcutsVector);
|
||||
}
|
||||
|
||||
void InputSourceKeyboard::handleEventKeyUp(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
if(key.repeat != 0)
|
||||
return; // ignore periodic event resends
|
||||
|
||||
assert(key.state == SDL_RELEASED);
|
||||
|
||||
auto shortcutsVector = GH.shortcuts().translateKeycode(key.keysym.sym);
|
||||
|
||||
GH.events().dispatchShortcutReleased(shortcutsVector);
|
||||
}
|
23
client/eventsSDL/InputSourceKeyboard.h
Normal file
23
client/eventsSDL/InputSourceKeyboard.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* InputSourceKeyboard.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct SDL_KeyboardEvent;
|
||||
|
||||
/// Class that handles keyboard input from SDL events
|
||||
class InputSourceKeyboard
|
||||
{
|
||||
public:
|
||||
InputSourceKeyboard();
|
||||
|
||||
void handleEventKeyDown(const SDL_KeyboardEvent & current);
|
||||
void handleEventKeyUp(const SDL_KeyboardEvent & current);
|
||||
};
|
72
client/eventsSDL/InputSourceMouse.cpp
Normal file
72
client/eventsSDL/InputSourceMouse.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* InputSourceMouse.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "InputSourceMouse.h"
|
||||
|
||||
#include "../../lib/Point.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/EventDispatcher.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
|
||||
void InputSourceMouse::handleEventMouseMotion(const SDL_MouseMotionEvent & motion)
|
||||
{
|
||||
GH.events().dispatchMouseMoved(Point(motion.x, motion.y));
|
||||
}
|
||||
|
||||
void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & button)
|
||||
{
|
||||
Point position(button.x, button.y);
|
||||
|
||||
switch(button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
if(button.clicks > 1)
|
||||
GH.events().dispatchMouseDoubleClick(position);
|
||||
else
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::LEFT, position);
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, position);
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::MIDDLE, position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InputSourceMouse::handleEventMouseWheel(const SDL_MouseWheelEvent & wheel)
|
||||
{
|
||||
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
|
||||
int x = 0, y = 0;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
|
||||
GH.events().dispatchMouseScrolled(Point(wheel.x, wheel.y), Point(x, y));
|
||||
}
|
||||
|
||||
void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & button)
|
||||
{
|
||||
Point position(button.x, button.y);
|
||||
|
||||
switch(button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::LEFT, position);
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, position);
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::MIDDLE, position);
|
||||
break;
|
||||
}
|
||||
}
|
25
client/eventsSDL/InputSourceMouse.h
Normal file
25
client/eventsSDL/InputSourceMouse.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* InputSourceMouse.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct SDL_MouseWheelEvent;
|
||||
struct SDL_MouseMotionEvent;
|
||||
struct SDL_MouseButtonEvent;
|
||||
|
||||
/// Class that handles mouse input from SDL events
|
||||
class InputSourceMouse
|
||||
{
|
||||
public:
|
||||
void handleEventMouseMotion(const SDL_MouseMotionEvent & current);
|
||||
void handleEventMouseButtonDown(const SDL_MouseButtonEvent & current);
|
||||
void handleEventMouseWheel(const SDL_MouseWheelEvent & current);
|
||||
void handleEventMouseButtonUp(const SDL_MouseButtonEvent & current);
|
||||
};
|
92
client/eventsSDL/InputSourceText.cpp
Normal file
92
client/eventsSDL/InputSourceText.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* InputSourceText.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "InputSourceText.h"
|
||||
|
||||
#include "../CMT.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/EventDispatcher.h"
|
||||
|
||||
#include "../../lib/Rect.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_render.h>
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
# include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
#ifdef VCMI_IOS
|
||||
# include "ios/utils.h"
|
||||
#endif
|
||||
|
||||
void InputSourceText::handleEventTextInput(const SDL_TextInputEvent & text)
|
||||
{
|
||||
GH.events().dispatchTextInput(text.text);
|
||||
}
|
||||
|
||||
void InputSourceText::handleEventTextEditing(const SDL_TextEditingEvent & text)
|
||||
{
|
||||
GH.events().dispatchTextEditing(text.text);
|
||||
}
|
||||
|
||||
void InputSourceText::startTextInput(const Rect & whereInput)
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
|
||||
// TODO ios: looks like SDL bug actually, try fixing there
|
||||
auto renderer = SDL_GetRenderer(mainWindow);
|
||||
float scaleX, scaleY;
|
||||
SDL_Rect viewport;
|
||||
SDL_RenderGetScale(renderer, &scaleX, &scaleY);
|
||||
SDL_RenderGetViewport(renderer, &viewport);
|
||||
|
||||
#ifdef VCMI_IOS
|
||||
const auto nativeScale = iOS_utils::screenScale();
|
||||
scaleX /= nativeScale;
|
||||
scaleY /= nativeScale;
|
||||
#endif
|
||||
|
||||
SDL_Rect rectInScreenCoordinates;
|
||||
rectInScreenCoordinates.x = (viewport.x + whereInput.x) * scaleX;
|
||||
rectInScreenCoordinates.y = (viewport.y + whereInput.y) * scaleY;
|
||||
rectInScreenCoordinates.w = whereInput.w * scaleX;
|
||||
rectInScreenCoordinates.h = whereInput.h * scaleY;
|
||||
|
||||
SDL_SetTextInputRect(&rectInScreenCoordinates);
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_FALSE)
|
||||
{
|
||||
SDL_StartTextInput();
|
||||
}
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputSourceText::stopTextInput()
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_TRUE)
|
||||
{
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
29
client/eventsSDL/InputSourceText.h
Normal file
29
client/eventsSDL/InputSourceText.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* InputSourceText.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class Rect;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
struct SDL_TextEditingEvent;
|
||||
struct SDL_TextInputEvent;
|
||||
|
||||
/// Class that handles text input (e.g. IME or direct input from physical keyboard) from SDL events
|
||||
class InputSourceText
|
||||
{
|
||||
public:
|
||||
void handleEventTextInput(const SDL_TextInputEvent & current);
|
||||
void handleEventTextEditing(const SDL_TextEditingEvent & current);
|
||||
|
||||
void startTextInput(const Rect & where);
|
||||
void stopTextInput();
|
||||
};
|
135
client/eventsSDL/InputSourceTouch.cpp
Normal file
135
client/eventsSDL/InputSourceTouch.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* InputSourceTouch.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "InputSourceTouch.h"
|
||||
|
||||
#include "InputHandler.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../CMT.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/EventDispatcher.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_hints.h>
|
||||
|
||||
InputSourceTouch::InputSourceTouch()
|
||||
: multifinger(false)
|
||||
, isPointerRelativeMode(settings["general"]["userRelativePointer"].Bool())
|
||||
{
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
}
|
||||
}
|
||||
|
||||
void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
GH.input().fakeMoveCursor(tfinger.dx, tfinger.dy);
|
||||
}
|
||||
}
|
||||
|
||||
void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
auto fingerCount = SDL_GetNumTouchFingers(tfinger.touchId);
|
||||
|
||||
multifinger = fingerCount > 1;
|
||||
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
if(tfinger.x > 0.5)
|
||||
{
|
||||
bool isRightClick = tfinger.y < 0.5;
|
||||
|
||||
fakeMouseButtonEventRelativeMode(true, isRightClick);
|
||||
}
|
||||
}
|
||||
#ifndef VCMI_IOS
|
||||
else if(fingerCount == 2)
|
||||
{
|
||||
Point position = convertTouchToMouse(tfinger);
|
||||
|
||||
GH.events().dispatchMouseMoved(position);
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, position);
|
||||
}
|
||||
#endif //VCMI_IOS
|
||||
}
|
||||
|
||||
void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
#ifndef VCMI_IOS
|
||||
auto fingerCount = SDL_GetNumTouchFingers(tfinger.touchId);
|
||||
#endif //VCMI_IOS
|
||||
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
if(tfinger.x > 0.5)
|
||||
{
|
||||
bool isRightClick = tfinger.y < 0.5;
|
||||
|
||||
fakeMouseButtonEventRelativeMode(false, isRightClick);
|
||||
}
|
||||
}
|
||||
#ifndef VCMI_IOS
|
||||
else if(multifinger)
|
||||
{
|
||||
Point position = convertTouchToMouse(tfinger);
|
||||
GH.events().dispatchMouseMoved(position);
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, position);
|
||||
multifinger = fingerCount != 0;
|
||||
}
|
||||
#endif //VCMI_IOS
|
||||
}
|
||||
|
||||
Point InputSourceTouch::convertTouchToMouse(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
return Point(tfinger.x * GH.screenDimensions().x, tfinger.y * GH.screenDimensions().y);
|
||||
}
|
||||
|
||||
void InputSourceTouch::fakeMouseButtonEventRelativeMode(bool down, bool right)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_MouseButtonEvent sme = {SDL_MOUSEBUTTONDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
if(!down)
|
||||
{
|
||||
sme.type = SDL_MOUSEBUTTONUP;
|
||||
}
|
||||
|
||||
sme.button = right ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT;
|
||||
|
||||
sme.x = GH.getCursorPosition().x;
|
||||
sme.y = GH.getCursorPosition().y;
|
||||
|
||||
float xScale, yScale;
|
||||
int w, h, rLogicalWidth, rLogicalHeight;
|
||||
|
||||
SDL_GetWindowSize(mainWindow, &w, &h);
|
||||
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
|
||||
SDL_RenderGetScale(mainRenderer, &xScale, &yScale);
|
||||
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
|
||||
moveCursorToPosition(Point((int)(sme.x * xScale) + (w - rLogicalWidth * xScale) / 2, (int)(sme.y * yScale + (h - rLogicalHeight * yScale) / 2)));
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
|
||||
|
||||
event.button = sme;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void InputSourceTouch::moveCursorToPosition(const Point & position)
|
||||
{
|
||||
SDL_WarpMouseInWindow(mainWindow, position.x, position.y);
|
||||
}
|
37
client/eventsSDL/InputSourceTouch.h
Normal file
37
client/eventsSDL/InputSourceTouch.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* InputSourceTouch.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class Point;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
struct SDL_TouchFingerEvent;
|
||||
|
||||
/// Class that handles touchscreen input from SDL events
|
||||
class InputSourceTouch
|
||||
{
|
||||
bool multifinger;
|
||||
bool isPointerRelativeMode;
|
||||
|
||||
/// moves mouse pointer into specified position inside vcmi window
|
||||
void moveCursorToPosition(const Point & position);
|
||||
Point convertTouchToMouse(const SDL_TouchFingerEvent & current);
|
||||
|
||||
void fakeMouseButtonEventRelativeMode(bool down, bool right);
|
||||
|
||||
public:
|
||||
InputSourceTouch();
|
||||
|
||||
void handleEventFingerMotion(const SDL_TouchFingerEvent & current);
|
||||
void handleEventFingerDown(const SDL_TouchFingerEvent & current);
|
||||
void handleEventFingerUp(const SDL_TouchFingerEvent & current);
|
||||
};
|
@ -10,11 +10,11 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "NotificationHandler.h"
|
||||
#include <SDL_video.h>
|
||||
#include <SDL_events.h>
|
||||
|
||||
#if defined(VCMI_WINDOWS)
|
||||
#include <SDL_syswm.h>
|
||||
#include <SDL_video.h>
|
||||
#include <SDL_events.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files:
|
87
client/eventsSDL/UserEventHandler.cpp
Normal file
87
client/eventsSDL/UserEventHandler.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* EventHandlerSDLUser.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "UserEventHandler.h"
|
||||
|
||||
#include "../CMT.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../mainmenu/CPrologEpilogVideo.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
|
||||
void UserEventHandler::handleUserEvent(const SDL_UserEvent & user)
|
||||
{
|
||||
switch(static_cast<EUserEvent>(user.code))
|
||||
{
|
||||
case EUserEvent::FORCE_QUIT:
|
||||
{
|
||||
handleQuit(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case EUserEvent::RETURN_TO_MAIN_MENU:
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("main");
|
||||
}
|
||||
break;
|
||||
case EUserEvent::RESTART_GAME:
|
||||
{
|
||||
CSH->sendRestartGame();
|
||||
}
|
||||
break;
|
||||
case EUserEvent::CAMPAIGN_START_SCENARIO:
|
||||
{
|
||||
CSH->campaignServerRestartLock.set(true);
|
||||
CSH->endGameplay();
|
||||
auto ourCampaign = std::shared_ptr<CCampaignState>(reinterpret_cast<CCampaignState *>(user.data1));
|
||||
auto & epilogue = ourCampaign->camp->scenarios[ourCampaign->mapsConquered.back()].epilog;
|
||||
auto finisher = [=]()
|
||||
{
|
||||
if(!ourCampaign->mapsRemaining.empty())
|
||||
{
|
||||
GH.windows().pushWindow(CMM);
|
||||
GH.windows().pushWindow(CMM->menu);
|
||||
CMM->openCampaignLobby(ourCampaign);
|
||||
}
|
||||
};
|
||||
if(epilogue.hasPrologEpilog)
|
||||
{
|
||||
GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher);
|
||||
}
|
||||
else
|
||||
{
|
||||
CSH->campaignServerRestartLock.waitUntil(false);
|
||||
finisher();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EUserEvent::RETURN_TO_MENU_LOAD:
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("load");
|
||||
break;
|
||||
case EUserEvent::FULLSCREEN_TOGGLED:
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.onScreenResize();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logGlobal->error("Unknown user event. Code %d", user.code);
|
||||
break;
|
||||
}
|
||||
}
|
20
client/eventsSDL/UserEventHandler.h
Normal file
20
client/eventsSDL/UserEventHandler.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* EventHandlerSDLUser.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct SDL_UserEvent;
|
||||
|
||||
/// Class for handling events of type SDL_UserEvent
|
||||
class UserEventHandler
|
||||
{
|
||||
public:
|
||||
void handleUserEvent(const SDL_UserEvent & current);
|
||||
};
|
@ -16,6 +16,8 @@
|
||||
#include "ShortcutHandler.h"
|
||||
#include "FramerateManager.h"
|
||||
#include "WindowHandler.h"
|
||||
#include "EventDispatcher.h"
|
||||
#include "../eventsSDL/InputHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../render/Colors.h"
|
||||
@ -29,23 +31,9 @@
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_timer.h>
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_keycode.h>
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
#ifdef VCMI_IOS
|
||||
#include "ios/utils.h"
|
||||
#endif
|
||||
|
||||
CGuiHandler GH;
|
||||
|
||||
extern std::queue<SDL_Event> SDLEventsQueue;
|
||||
extern boost::mutex eventsM;
|
||||
|
||||
boost::thread_specific_ptr<bool> inGuiThread;
|
||||
|
||||
SObjectConstruction::SObjectConstruction(CIntObject *obj)
|
||||
@ -77,515 +65,44 @@ SSetCaptureState::~SSetCaptureState()
|
||||
GH.defActionsDef = prevActions;
|
||||
}
|
||||
|
||||
static inline void
|
||||
processList(const ui16 mask, const ui16 flag, std::list<CIntObject*> *lst, std::function<void (std::list<CIntObject*> *)> cb)
|
||||
{
|
||||
if (mask & flag)
|
||||
cb(lst);
|
||||
}
|
||||
|
||||
void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb)
|
||||
{
|
||||
processList(CIntObject::LCLICK,activityFlag,&lclickable,cb);
|
||||
processList(CIntObject::RCLICK,activityFlag,&rclickable,cb);
|
||||
processList(CIntObject::MCLICK,activityFlag,&mclickable,cb);
|
||||
processList(CIntObject::HOVER,activityFlag,&hoverable,cb);
|
||||
processList(CIntObject::MOVE,activityFlag,&motioninterested,cb);
|
||||
processList(CIntObject::KEYBOARD,activityFlag,&keyinterested,cb);
|
||||
processList(CIntObject::TIME,activityFlag,&timeinterested,cb);
|
||||
processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);
|
||||
processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,cb);
|
||||
processList(CIntObject::TEXTINPUT,activityFlag,&textInterested,cb);
|
||||
}
|
||||
|
||||
void CGuiHandler::init()
|
||||
{
|
||||
inputHandlerInstance = std::make_unique<InputHandler>();
|
||||
eventDispatcherInstance = std::make_unique<EventDispatcher>();
|
||||
windowHandlerInstance = std::make_unique<WindowHandler>();
|
||||
screenHandlerInstance = std::make_unique<ScreenHandler>();
|
||||
shortcutsHandlerInstance = std::make_unique<ShortcutHandler>();
|
||||
framerateManagerInstance = std::make_unique<FramerateManager>(settings["video"]["targetfps"].Integer());
|
||||
|
||||
isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool();
|
||||
pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
||||
}
|
||||
|
||||
void CGuiHandler::handleElementActivate(CIntObject * elem, ui16 activityFlag)
|
||||
{
|
||||
processLists(activityFlag,[&](std::list<CIntObject*> * lst){
|
||||
lst->push_front(elem);
|
||||
});
|
||||
elem->active_m |= activityFlag;
|
||||
}
|
||||
|
||||
void CGuiHandler::handleElementDeActivate(CIntObject * elem, ui16 activityFlag)
|
||||
{
|
||||
processLists(activityFlag,[&](std::list<CIntObject*> * lst){
|
||||
auto hlp = std::find(lst->begin(),lst->end(),elem);
|
||||
assert(hlp != lst->end());
|
||||
lst->erase(hlp);
|
||||
});
|
||||
elem->active_m &= ~activityFlag;
|
||||
}
|
||||
|
||||
void CGuiHandler::updateTime()
|
||||
{
|
||||
int ms = framerateManager().getElapsedMilliseconds();
|
||||
std::list<CIntObject*> hlp = timeinterested;
|
||||
for (auto & elem : hlp)
|
||||
{
|
||||
if(!vstd::contains(timeinterested,elem)) continue;
|
||||
(elem)->tick(ms);
|
||||
}
|
||||
}
|
||||
|
||||
void CGuiHandler::handleEvents()
|
||||
{
|
||||
events().dispatchTimer(framerate().getElapsedMilliseconds());
|
||||
|
||||
//player interface may want special event handling
|
||||
if(nullptr != LOCPLINT && LOCPLINT->capturedAllEvents())
|
||||
return;
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(eventsM);
|
||||
while(!SDLEventsQueue.empty())
|
||||
{
|
||||
continueEventHandling = true;
|
||||
SDL_Event currentEvent = SDLEventsQueue.front();
|
||||
|
||||
if (currentEvent.type == SDL_MOUSEMOTION)
|
||||
{
|
||||
cursorPosition = Point(currentEvent.motion.x, currentEvent.motion.y);
|
||||
mouseButtonsMask = currentEvent.motion.state;
|
||||
}
|
||||
SDLEventsQueue.pop();
|
||||
|
||||
// In a sequence of mouse motion events, skip all but the last one.
|
||||
// This prevents freezes when every motion event takes longer to handle than interval at which
|
||||
// the events arrive (like dragging on the minimap in world view, with redraw at every event)
|
||||
// so that the events would start piling up faster than they can be processed.
|
||||
if ((currentEvent.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION))
|
||||
continue;
|
||||
|
||||
handleCurrentEvent(currentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void CGuiHandler::convertTouchToMouse(SDL_Event * current)
|
||||
{
|
||||
int rLogicalWidth, rLogicalHeight;
|
||||
|
||||
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
|
||||
|
||||
int adjustedMouseY = (int)(current->tfinger.y * rLogicalHeight);
|
||||
int adjustedMouseX = (int)(current->tfinger.x * rLogicalWidth);
|
||||
|
||||
current->button.x = adjustedMouseX;
|
||||
current->motion.x = adjustedMouseX;
|
||||
current->button.y = adjustedMouseY;
|
||||
current->motion.y = adjustedMouseY;
|
||||
}
|
||||
|
||||
void CGuiHandler::fakeMoveCursor(float dx, float dy)
|
||||
{
|
||||
int x, y, w, h;
|
||||
|
||||
SDL_Event event;
|
||||
SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
sme.state = SDL_GetMouseState(&x, &y);
|
||||
SDL_GetWindowSize(mainWindow, &w, &h);
|
||||
|
||||
sme.x = CCS->curh->position().x + (int)(GH.pointerSpeedMultiplier * w * dx);
|
||||
sme.y = CCS->curh->position().y + (int)(GH.pointerSpeedMultiplier * h * dy);
|
||||
|
||||
vstd::abetween(sme.x, 0, w);
|
||||
vstd::abetween(sme.y, 0, h);
|
||||
|
||||
event.motion = sme;
|
||||
SDL_PushEvent(&event);
|
||||
input().processEvents();
|
||||
}
|
||||
|
||||
void CGuiHandler::fakeMouseMove()
|
||||
{
|
||||
fakeMoveCursor(0, 0);
|
||||
input().fakeMoveCursor(0, 0);
|
||||
}
|
||||
|
||||
void CGuiHandler::startTextInput(const Rect & whereInput)
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
|
||||
// TODO ios: looks like SDL bug actually, try fixing there
|
||||
auto renderer = SDL_GetRenderer(mainWindow);
|
||||
float scaleX, scaleY;
|
||||
SDL_Rect viewport;
|
||||
SDL_RenderGetScale(renderer, &scaleX, &scaleY);
|
||||
SDL_RenderGetViewport(renderer, &viewport);
|
||||
|
||||
#ifdef VCMI_IOS
|
||||
const auto nativeScale = iOS_utils::screenScale();
|
||||
scaleX /= nativeScale;
|
||||
scaleY /= nativeScale;
|
||||
#endif
|
||||
|
||||
SDL_Rect rectInScreenCoordinates;
|
||||
rectInScreenCoordinates.x = (viewport.x + whereInput.x) * scaleX;
|
||||
rectInScreenCoordinates.y = (viewport.y + whereInput.y) * scaleY;
|
||||
rectInScreenCoordinates.w = whereInput.w * scaleX;
|
||||
rectInScreenCoordinates.h = whereInput.h * scaleY;
|
||||
|
||||
SDL_SetTextInputRect(&rectInScreenCoordinates);
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_FALSE)
|
||||
{
|
||||
SDL_StartTextInput();
|
||||
}
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
input().startTextInput(whereInput);
|
||||
}
|
||||
|
||||
void CGuiHandler::stopTextInput()
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_TRUE)
|
||||
{
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CGuiHandler::fakeMouseButtonEventRelativeMode(bool down, bool right)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_MouseButtonEvent sme = {SDL_MOUSEBUTTONDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
if(!down)
|
||||
{
|
||||
sme.type = SDL_MOUSEBUTTONUP;
|
||||
}
|
||||
|
||||
sme.button = right ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT;
|
||||
|
||||
sme.x = CCS->curh->position().x;
|
||||
sme.y = CCS->curh->position().y;
|
||||
|
||||
float xScale, yScale;
|
||||
int w, h, rLogicalWidth, rLogicalHeight;
|
||||
|
||||
SDL_GetWindowSize(mainWindow, &w, &h);
|
||||
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
|
||||
SDL_RenderGetScale(mainRenderer, &xScale, &yScale);
|
||||
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
|
||||
moveCursorToPosition( Point(
|
||||
(int)(sme.x * xScale) + (w - rLogicalWidth * xScale) / 2,
|
||||
(int)(sme.y * yScale + (h - rLogicalHeight * yScale) / 2)));
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
|
||||
|
||||
event.button = sme;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void CGuiHandler::handleCurrentEvent( SDL_Event & current )
|
||||
{
|
||||
if(current.type == SDL_KEYDOWN || current.type == SDL_KEYUP)
|
||||
{
|
||||
SDL_KeyboardEvent key = current.key;
|
||||
|
||||
if (key.repeat != 0)
|
||||
return; // ignore periodic event resends
|
||||
|
||||
if(current.type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
|
||||
{
|
||||
//TODO: we need some central place for all interface-independent hotkeys
|
||||
Settings s = settings.write["session"];
|
||||
switch(key.keysym.sym)
|
||||
{
|
||||
case SDLK_F5:
|
||||
if(settings["session"]["spectate-locked-pim"].Bool())
|
||||
LOCPLINT->pim->unlock();
|
||||
else
|
||||
LOCPLINT->pim->lock();
|
||||
s["spectate-locked-pim"].Bool() = !settings["session"]["spectate-locked-pim"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F6:
|
||||
s["spectate-ignore-hero"].Bool() = !settings["session"]["spectate-ignore-hero"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F7:
|
||||
s["spectate-skip-battle"].Bool() = !settings["session"]["spectate-skip-battle"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F8:
|
||||
s["spectate-skip-battle-result"].Bool() = !settings["session"]["spectate-skip-battle-result"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F9:
|
||||
//not working yet since CClient::run remain locked after BattleInterface removal
|
||||
// if(LOCPLINT->battleInt)
|
||||
// {
|
||||
// GH.windows().popInts(1);
|
||||
// vstd::clear_pointer(LOCPLINT->battleInt);
|
||||
// }
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto shortcutsVector = shortcutsHandler().translateKeycode(key.keysym.sym);
|
||||
|
||||
bool keysCaptured = false;
|
||||
for(auto i = keyinterested.begin(); i != keyinterested.end() && continueEventHandling; i++)
|
||||
{
|
||||
for (EShortcut shortcut : shortcutsVector)
|
||||
{
|
||||
if((*i)->captureThisKey(shortcut))
|
||||
{
|
||||
keysCaptured = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::list<CIntObject*> miCopy = keyinterested;
|
||||
for(auto i = miCopy.begin(); i != miCopy.end() && continueEventHandling; i++)
|
||||
{
|
||||
for (EShortcut shortcut : shortcutsVector)
|
||||
{
|
||||
if(vstd::contains(keyinterested,*i) && (!keysCaptured || (*i)->captureThisKey(shortcut)))
|
||||
{
|
||||
if (key.state == SDL_PRESSED)
|
||||
(**i).keyPressed(shortcut);
|
||||
if (key.state == SDL_RELEASED)
|
||||
(**i).keyReleased(shortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current.type == SDL_MOUSEMOTION)
|
||||
{
|
||||
handleMouseMotion(current);
|
||||
}
|
||||
else if(current.type == SDL_MOUSEBUTTONDOWN)
|
||||
{
|
||||
switch(current.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
{
|
||||
auto doubleClicked = false;
|
||||
if(lastClick == getCursorPosition() && (SDL_GetTicks() - lastClickTime) < 300)
|
||||
{
|
||||
std::list<CIntObject*> hlp = doubleClickInterested;
|
||||
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
|
||||
{
|
||||
if(!vstd::contains(doubleClickInterested, *i)) continue;
|
||||
if((*i)->pos.isInside(current.motion.x, current.motion.y))
|
||||
{
|
||||
(*i)->onDoubleClick();
|
||||
doubleClicked = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lastClick = current.motion;
|
||||
lastClickTime = SDL_GetTicks();
|
||||
|
||||
if(!doubleClicked)
|
||||
handleMouseButtonClick(lclickable, MouseButton::LEFT, true);
|
||||
break;
|
||||
}
|
||||
case SDL_BUTTON_RIGHT:
|
||||
handleMouseButtonClick(rclickable, MouseButton::RIGHT, true);
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
handleMouseButtonClick(mclickable, MouseButton::MIDDLE, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(current.type == SDL_MOUSEWHEEL)
|
||||
{
|
||||
std::list<CIntObject*> hlp = wheelInterested;
|
||||
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
|
||||
{
|
||||
if(!vstd::contains(wheelInterested,*i)) continue;
|
||||
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
|
||||
int x = 0, y = 0;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
(*i)->wheelScrolled(current.wheel.y < 0, (*i)->pos.isInside(x, y));
|
||||
}
|
||||
}
|
||||
else if(current.type == SDL_TEXTINPUT)
|
||||
{
|
||||
for(auto it : textInterested)
|
||||
{
|
||||
it->textInputed(current.text.text);
|
||||
}
|
||||
}
|
||||
else if(current.type == SDL_TEXTEDITING)
|
||||
{
|
||||
for(auto it : textInterested)
|
||||
{
|
||||
it->textEdited(current.edit.text);
|
||||
}
|
||||
}
|
||||
else if(current.type == SDL_MOUSEBUTTONUP)
|
||||
{
|
||||
if(!multifinger)
|
||||
{
|
||||
switch(current.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
handleMouseButtonClick(lclickable, MouseButton::LEFT, false);
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
handleMouseButtonClick(rclickable, MouseButton::RIGHT, false);
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
handleMouseButtonClick(mclickable, MouseButton::MIDDLE, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current.type == SDL_FINGERMOTION)
|
||||
{
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
fakeMoveCursor(current.tfinger.dx, current.tfinger.dy);
|
||||
}
|
||||
}
|
||||
else if(current.type == SDL_FINGERDOWN)
|
||||
{
|
||||
auto fingerCount = SDL_GetNumTouchFingers(current.tfinger.touchId);
|
||||
|
||||
multifinger = fingerCount > 1;
|
||||
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
if(current.tfinger.x > 0.5)
|
||||
{
|
||||
bool isRightClick = current.tfinger.y < 0.5;
|
||||
|
||||
fakeMouseButtonEventRelativeMode(true, isRightClick);
|
||||
}
|
||||
}
|
||||
#ifndef VCMI_IOS
|
||||
else if(fingerCount == 2)
|
||||
{
|
||||
convertTouchToMouse(¤t);
|
||||
handleMouseMotion(current);
|
||||
handleMouseButtonClick(rclickable, MouseButton::RIGHT, true);
|
||||
}
|
||||
#endif //VCMI_IOS
|
||||
}
|
||||
else if(current.type == SDL_FINGERUP)
|
||||
{
|
||||
#ifndef VCMI_IOS
|
||||
auto fingerCount = SDL_GetNumTouchFingers(current.tfinger.touchId);
|
||||
#endif //VCMI_IOS
|
||||
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
if(current.tfinger.x > 0.5)
|
||||
{
|
||||
bool isRightClick = current.tfinger.y < 0.5;
|
||||
|
||||
fakeMouseButtonEventRelativeMode(false, isRightClick);
|
||||
}
|
||||
}
|
||||
#ifndef VCMI_IOS
|
||||
else if(multifinger)
|
||||
{
|
||||
convertTouchToMouse(¤t);
|
||||
handleMouseMotion(current);
|
||||
handleMouseButtonClick(rclickable, MouseButton::RIGHT, false);
|
||||
multifinger = fingerCount != 0;
|
||||
}
|
||||
#endif //VCMI_IOS
|
||||
}
|
||||
} //event end
|
||||
|
||||
void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, MouseButton btn, bool isPressed)
|
||||
{
|
||||
auto hlp = interestedObjs;
|
||||
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
|
||||
{
|
||||
if(!vstd::contains(interestedObjs, *i)) continue;
|
||||
|
||||
auto prev = (*i)->mouseState(btn);
|
||||
if(!isPressed)
|
||||
(*i)->updateMouseState(btn, isPressed);
|
||||
if((*i)->pos.isInside(getCursorPosition()))
|
||||
{
|
||||
if(isPressed)
|
||||
(*i)->updateMouseState(btn, isPressed);
|
||||
(*i)->click(btn, isPressed, prev);
|
||||
}
|
||||
else if(!isPressed)
|
||||
(*i)->click(btn, boost::logic::indeterminate, prev);
|
||||
}
|
||||
}
|
||||
|
||||
void CGuiHandler::handleMouseMotion(const SDL_Event & current)
|
||||
{
|
||||
//sending active, hovered hoverable objects hover() call
|
||||
std::vector<CIntObject*> hlp;
|
||||
|
||||
auto hoverableCopy = hoverable;
|
||||
for(auto & elem : hoverableCopy)
|
||||
{
|
||||
if(elem->pos.isInside(getCursorPosition()))
|
||||
{
|
||||
if (!(elem)->hovered)
|
||||
hlp.push_back((elem));
|
||||
}
|
||||
else if ((elem)->hovered)
|
||||
{
|
||||
(elem)->hover(false);
|
||||
(elem)->hovered = false;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto & elem : hlp)
|
||||
{
|
||||
elem->hover(true);
|
||||
elem->hovered = true;
|
||||
}
|
||||
|
||||
// do not send motion events for events outside our window
|
||||
//if (current.motion.windowID == 0)
|
||||
handleMoveInterested(current.motion);
|
||||
}
|
||||
|
||||
void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
|
||||
{
|
||||
//sending active, MotionInterested objects mouseMoved() call
|
||||
std::list<CIntObject*> miCopy = motioninterested;
|
||||
for(auto & elem : miCopy)
|
||||
{
|
||||
if(elem->strongInterest || Rect::createAround(elem->pos, 1).isInside( motion.x, motion.y)) //checking bounds including border fixes bug #2476
|
||||
{
|
||||
(elem)->mouseMoved(Point(motion.x, motion.y));
|
||||
}
|
||||
}
|
||||
input().stopTextInput();
|
||||
}
|
||||
|
||||
void CGuiHandler::renderFrame()
|
||||
{
|
||||
|
||||
// Updating GUI requires locking pim mutex (that protects screen and GUI state).
|
||||
// During game:
|
||||
// When ending the game, the pim mutex might be hold by other thread,
|
||||
@ -619,17 +136,12 @@ void CGuiHandler::renderFrame()
|
||||
windows().onFrameRendered();
|
||||
}
|
||||
|
||||
framerateManager().framerateDelay(); // holds a constant FPS
|
||||
framerate().framerateDelay(); // holds a constant FPS
|
||||
}
|
||||
|
||||
CGuiHandler::CGuiHandler()
|
||||
: lastClick(-500, -500)
|
||||
, lastClickTime(0)
|
||||
, defActionsDef(0)
|
||||
: defActionsDef(0)
|
||||
, captureChildren(false)
|
||||
, multifinger(false)
|
||||
, mouseButtonsMask(0)
|
||||
, continueEventHandling(true)
|
||||
, curInt(nullptr)
|
||||
, fakeStatusBar(std::make_shared<EmptyStatusBar>())
|
||||
, terminate_cond (new CondSh<bool>(false))
|
||||
@ -641,50 +153,36 @@ CGuiHandler::~CGuiHandler()
|
||||
delete terminate_cond;
|
||||
}
|
||||
|
||||
ShortcutHandler & CGuiHandler::shortcutsHandler()
|
||||
ShortcutHandler & CGuiHandler::shortcuts()
|
||||
{
|
||||
assert(shortcutsHandlerInstance);
|
||||
return *shortcutsHandlerInstance;
|
||||
}
|
||||
|
||||
FramerateManager & CGuiHandler::framerateManager()
|
||||
FramerateManager & CGuiHandler::framerate()
|
||||
{
|
||||
assert(framerateManagerInstance);
|
||||
return *framerateManagerInstance;
|
||||
}
|
||||
|
||||
void CGuiHandler::moveCursorToPosition(const Point & position)
|
||||
{
|
||||
SDL_WarpMouseInWindow(mainWindow, position.x, position.y);
|
||||
}
|
||||
|
||||
bool CGuiHandler::isKeyboardCtrlDown() const
|
||||
{
|
||||
#ifdef VCMI_MAC
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LGUI] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RGUI];
|
||||
#else
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RCTRL];
|
||||
#endif
|
||||
return inputHandlerInstance->isKeyboardCtrlDown();
|
||||
}
|
||||
|
||||
bool CGuiHandler::isKeyboardAltDown() const
|
||||
{
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LALT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RALT];
|
||||
return inputHandlerInstance->isKeyboardAltDown();
|
||||
}
|
||||
|
||||
bool CGuiHandler::isKeyboardShiftDown() const
|
||||
{
|
||||
return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LSHIFT] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RSHIFT];
|
||||
}
|
||||
|
||||
void CGuiHandler::breakEventHandling()
|
||||
{
|
||||
continueEventHandling = false;
|
||||
return inputHandlerInstance->isKeyboardShiftDown();
|
||||
}
|
||||
|
||||
const Point & CGuiHandler::getCursorPosition() const
|
||||
{
|
||||
return cursorPosition;
|
||||
return inputHandlerInstance->getCursorPosition();
|
||||
}
|
||||
|
||||
Point CGuiHandler::screenDimensions() const
|
||||
@ -692,21 +190,9 @@ Point CGuiHandler::screenDimensions() const
|
||||
return Point(screen->w, screen->h);
|
||||
}
|
||||
|
||||
bool CGuiHandler::isMouseButtonPressed() const
|
||||
{
|
||||
return mouseButtonsMask > 0;
|
||||
}
|
||||
|
||||
bool CGuiHandler::isMouseButtonPressed(MouseButton button) const
|
||||
{
|
||||
static_assert(static_cast<uint32_t>(MouseButton::LEFT) == SDL_BUTTON_LEFT, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::MIDDLE) == SDL_BUTTON_MIDDLE, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::RIGHT) == SDL_BUTTON_RIGHT, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::EXTRA1) == SDL_BUTTON_X1, "mismatch between VCMI and SDL enum!");
|
||||
static_assert(static_cast<uint32_t>(MouseButton::EXTRA2) == SDL_BUTTON_X2, "mismatch between VCMI and SDL enum!");
|
||||
|
||||
uint32_t index = static_cast<uint32_t>(button);
|
||||
return mouseButtonsMask & SDL_BUTTON(index);
|
||||
return inputHandlerInstance->isMouseButtonPressed(button);
|
||||
}
|
||||
|
||||
void CGuiHandler::drawFPSCounter()
|
||||
@ -714,7 +200,7 @@ void CGuiHandler::drawFPSCounter()
|
||||
static SDL_Rect overlay = { 0, 0, 64, 32};
|
||||
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
|
||||
SDL_FillRect(screen, &overlay, black);
|
||||
std::string fps = std::to_string(framerateManager().getFramerate());
|
||||
std::string fps = std::to_string(framerate().getFramerate());
|
||||
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10));
|
||||
}
|
||||
|
||||
@ -725,16 +211,12 @@ bool CGuiHandler::amIGuiThread()
|
||||
|
||||
void CGuiHandler::pushUserEvent(EUserEvent usercode)
|
||||
{
|
||||
pushUserEvent(usercode, nullptr);
|
||||
inputHandlerInstance->pushUserEvent(usercode, nullptr);
|
||||
}
|
||||
|
||||
void CGuiHandler::pushUserEvent(EUserEvent usercode, void * userdata)
|
||||
{
|
||||
SDL_Event event;
|
||||
event.type = SDL_USEREVENT;
|
||||
event.user.code = static_cast<int32_t>(usercode);
|
||||
event.user.data1 = userdata;
|
||||
SDL_PushEvent(&event);
|
||||
inputHandlerInstance->pushUserEvent(usercode, userdata);
|
||||
}
|
||||
|
||||
IScreenHandler & CGuiHandler::screenHandler()
|
||||
@ -742,6 +224,16 @@ IScreenHandler & CGuiHandler::screenHandler()
|
||||
return *screenHandlerInstance;
|
||||
}
|
||||
|
||||
EventDispatcher & CGuiHandler::events()
|
||||
{
|
||||
return *eventDispatcherInstance;
|
||||
}
|
||||
|
||||
InputHandler & CGuiHandler::input()
|
||||
{
|
||||
return *inputHandlerInstance;
|
||||
}
|
||||
|
||||
WindowHandler & CGuiHandler::windows()
|
||||
{
|
||||
assert(windowHandlerInstance);
|
||||
|
@ -9,48 +9,38 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "MouseButton.h"
|
||||
#include "../../lib/Point.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
template <typename T> struct CondSh;
|
||||
class Point;
|
||||
class Rect;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
union SDL_Event;
|
||||
struct SDL_MouseMotionEvent;
|
||||
|
||||
enum class MouseButton;
|
||||
class ShortcutHandler;
|
||||
class FramerateManager;
|
||||
class IStatusBar;
|
||||
class CIntObject;
|
||||
class IUpdateable;
|
||||
class IShowActivatable;
|
||||
class IShowable;
|
||||
class IScreenHandler;
|
||||
class WindowHandler;
|
||||
class EventDispatcher;
|
||||
class InputHandler;
|
||||
|
||||
// TODO: event handling need refactoring
|
||||
// TODO: event handling need refactoring. Perhaps convert into delayed function call?
|
||||
enum class EUserEvent
|
||||
{
|
||||
/*CHANGE_SCREEN_RESOLUTION = 1,*/
|
||||
RETURN_TO_MAIN_MENU = 2,
|
||||
//STOP_CLIENT = 3,
|
||||
RESTART_GAME = 4,
|
||||
RETURN_TO_MAIN_MENU,
|
||||
RESTART_GAME,
|
||||
RETURN_TO_MENU_LOAD,
|
||||
FULLSCREEN_TOGGLED,
|
||||
CAMPAIGN_START_SCENARIO,
|
||||
FORCE_QUIT, //quit client without question
|
||||
FORCE_QUIT,
|
||||
};
|
||||
|
||||
// Handles GUI logic and drawing
|
||||
class CGuiHandler
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
private:
|
||||
/// Fake no-op version status bar, for use in windows that have no status bar
|
||||
std::shared_ptr<IStatusBar> fakeStatusBar;
|
||||
@ -58,57 +48,27 @@ private:
|
||||
/// Status bar of current window, if any. Uses weak_ptr to allow potential hanging reference after owned window has been deleted
|
||||
std::weak_ptr<IStatusBar> currentStatusBar;
|
||||
|
||||
Point cursorPosition;
|
||||
uint32_t mouseButtonsMask;
|
||||
|
||||
std::unique_ptr<ShortcutHandler> shortcutsHandlerInstance;
|
||||
std::unique_ptr<WindowHandler> windowHandlerInstance;
|
||||
|
||||
std::atomic<bool> continueEventHandling;
|
||||
using CIntObjectList = std::list<CIntObject *>;
|
||||
|
||||
//active GUI elements (listening for events
|
||||
CIntObjectList lclickable;
|
||||
CIntObjectList rclickable;
|
||||
CIntObjectList mclickable;
|
||||
CIntObjectList hoverable;
|
||||
CIntObjectList keyinterested;
|
||||
CIntObjectList motioninterested;
|
||||
CIntObjectList timeinterested;
|
||||
CIntObjectList wheelInterested;
|
||||
CIntObjectList doubleClickInterested;
|
||||
CIntObjectList textInterested;
|
||||
|
||||
std::unique_ptr<IScreenHandler> screenHandlerInstance;
|
||||
std::unique_ptr<FramerateManager> framerateManagerInstance;
|
||||
|
||||
void handleMouseButtonClick(CIntObjectList & interestedObjs, MouseButton btn, bool isPressed);
|
||||
void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
|
||||
void handleCurrentEvent(SDL_Event ¤t);
|
||||
void handleMouseMotion(const SDL_Event & current);
|
||||
void handleMoveInterested( const SDL_MouseMotionEvent & motion );
|
||||
void convertTouchToMouse(SDL_Event * current);
|
||||
void fakeMoveCursor(float dx, float dy);
|
||||
void fakeMouseButtonEventRelativeMode(bool down, bool right);
|
||||
std::unique_ptr<EventDispatcher> eventDispatcherInstance;
|
||||
std::unique_ptr<InputHandler> inputHandlerInstance;
|
||||
|
||||
public:
|
||||
void handleElementActivate(CIntObject * elem, ui16 activityFlag);
|
||||
void handleElementDeActivate(CIntObject * elem, ui16 activityFlag);
|
||||
public:
|
||||
|
||||
/// returns current position of mouse cursor, relative to vcmi window
|
||||
const Point & getCursorPosition() const;
|
||||
|
||||
ShortcutHandler & shortcutsHandler();
|
||||
FramerateManager & framerateManager();
|
||||
ShortcutHandler & shortcuts();
|
||||
FramerateManager & framerate();
|
||||
EventDispatcher & events();
|
||||
InputHandler & input();
|
||||
|
||||
/// Returns current logical screen dimensions
|
||||
/// May not match size of window if user has UI scaling different from 100%
|
||||
Point screenDimensions() const;
|
||||
|
||||
/// returns true if at least one mouse button is pressed
|
||||
bool isMouseButtonPressed() const;
|
||||
|
||||
/// returns true if specified mouse button is pressed
|
||||
bool isMouseButtonPressed(MouseButton button) const;
|
||||
|
||||
@ -120,9 +80,6 @@ public:
|
||||
void startTextInput(const Rect & where);
|
||||
void stopTextInput();
|
||||
|
||||
/// moves mouse pointer into specified position inside vcmi window
|
||||
void moveCursorToPosition(const Point & position);
|
||||
|
||||
IScreenHandler & screenHandler();
|
||||
|
||||
WindowHandler & windows();
|
||||
@ -135,12 +92,6 @@ public:
|
||||
|
||||
IUpdateable *curInt;
|
||||
|
||||
Point lastClick;
|
||||
unsigned lastClickTime;
|
||||
bool multifinger;
|
||||
bool isPointerRelativeMode;
|
||||
float pointerSpeedMultiplier;
|
||||
|
||||
ui8 defActionsDef; //default auto actions
|
||||
bool captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
|
||||
std::list<CIntObject *> createdObj; //stack of objs being created
|
||||
@ -154,15 +105,13 @@ public:
|
||||
/// called whenever user selects different resolution, requiring to center/resize all windows
|
||||
void onScreenResize();
|
||||
|
||||
void updateTime(); //handles timeInterested
|
||||
void handleEvents(); //takes events from queue and calls interested objects
|
||||
void fakeMouseMove();
|
||||
void breakEventHandling(); //current event won't be propagated anymore
|
||||
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen
|
||||
|
||||
static bool amIGuiThread();
|
||||
static void pushUserEvent(EUserEvent usercode);
|
||||
static void pushUserEvent(EUserEvent usercode, void * userdata);
|
||||
bool amIGuiThread();
|
||||
void pushUserEvent(EUserEvent usercode);
|
||||
void pushUserEvent(EUserEvent usercode, void * userdata);
|
||||
|
||||
CondSh<bool> * terminate_cond; // confirm termination
|
||||
};
|
||||
|
@ -17,20 +17,12 @@
|
||||
#include "../windows/CMessage.h"
|
||||
#include "../CMT.h"
|
||||
|
||||
#include <SDL_pixels.h>
|
||||
|
||||
IShowActivatable::IShowActivatable()
|
||||
{
|
||||
type = 0;
|
||||
}
|
||||
|
||||
CIntObject::CIntObject(int used_, Point pos_):
|
||||
parent_m(nullptr),
|
||||
active_m(0),
|
||||
parent(parent_m),
|
||||
active(active_m)
|
||||
type(0)
|
||||
{
|
||||
hovered = captureAllKeys = strongInterest = false;
|
||||
captureAllKeys = false;
|
||||
used = used_;
|
||||
|
||||
recActions = defActions = GH.defActionsDef;
|
||||
@ -46,7 +38,7 @@ CIntObject::CIntObject(int used_, Point pos_):
|
||||
|
||||
CIntObject::~CIntObject()
|
||||
{
|
||||
if(active_m)
|
||||
if(isActive())
|
||||
deactivate();
|
||||
|
||||
while(!children.empty())
|
||||
@ -76,25 +68,16 @@ void CIntObject::showAll(SDL_Surface * to)
|
||||
for(auto & elem : children)
|
||||
if(elem->recActions & SHOWALL)
|
||||
elem->showAll(to);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void CIntObject::activate()
|
||||
{
|
||||
if (active_m)
|
||||
{
|
||||
if ((used | GENERAL) == active_m)
|
||||
return;
|
||||
else
|
||||
{
|
||||
logGlobal->warn("Warning: IntObject re-activated with mismatching used and active");
|
||||
deactivate(); //FIXME: better to avoid such possibility at all
|
||||
}
|
||||
}
|
||||
if (isActive())
|
||||
return;
|
||||
|
||||
active_m |= GENERAL;
|
||||
activate(used);
|
||||
activateEvents(used | GENERAL);
|
||||
assert(isActive());
|
||||
|
||||
if(defActions & ACTIVATE)
|
||||
for(auto & elem : children)
|
||||
@ -102,20 +85,14 @@ void CIntObject::activate()
|
||||
elem->activate();
|
||||
}
|
||||
|
||||
void CIntObject::activate(ui16 what)
|
||||
{
|
||||
GH.handleElementActivate(this, what);
|
||||
}
|
||||
|
||||
void CIntObject::deactivate()
|
||||
{
|
||||
if (!active_m)
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
active_m &= ~ GENERAL;
|
||||
deactivate(active_m);
|
||||
deactivateEvents(ALL);
|
||||
|
||||
assert(!active_m);
|
||||
assert(!isActive());
|
||||
|
||||
if(defActions & DEACTIVATE)
|
||||
for(auto & elem : children)
|
||||
@ -123,65 +100,33 @@ void CIntObject::deactivate()
|
||||
elem->deactivate();
|
||||
}
|
||||
|
||||
void CIntObject::deactivate(ui16 what)
|
||||
{
|
||||
GH.handleElementDeActivate(this, what);
|
||||
}
|
||||
|
||||
void CIntObject::click(MouseButton btn, tribool down, bool previousState)
|
||||
{
|
||||
switch(btn)
|
||||
{
|
||||
default:
|
||||
case MouseButton::LEFT:
|
||||
clickLeft(down, previousState);
|
||||
break;
|
||||
case MouseButton::MIDDLE:
|
||||
clickMiddle(down, previousState);
|
||||
break;
|
||||
case MouseButton::RIGHT:
|
||||
clickRight(down, previousState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CIntObject::printAtLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst)
|
||||
{
|
||||
graphics->fonts[font]->renderTextLeft(dst, text, kolor, Point(pos.x + x, pos.y + y));
|
||||
}
|
||||
|
||||
void CIntObject::printAtMiddleLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst)
|
||||
{
|
||||
printAtMiddleLoc(text, Point(x,y), font, kolor, dst);
|
||||
}
|
||||
|
||||
void CIntObject::printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color kolor, SDL_Surface * dst)
|
||||
void CIntObject::printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, const SDL_Color & kolor, SDL_Surface * dst)
|
||||
{
|
||||
graphics->fonts[font]->renderTextCenter(dst, text, kolor, pos.topLeft() + p);
|
||||
}
|
||||
|
||||
void CIntObject::printAtMiddleWBLoc( const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst)
|
||||
void CIntObject::printAtMiddleWBLoc( const std::string & text, const Point &p, EFonts font, int charpr, const SDL_Color & kolor, SDL_Surface * dst)
|
||||
{
|
||||
graphics->fonts[font]->renderTextLinesCenter(dst, CMessage::breakText(text, charpr, font), kolor, Point(pos.x + x, pos.y + y));
|
||||
graphics->fonts[font]->renderTextLinesCenter(dst, CMessage::breakText(text, charpr, font), kolor, pos.topLeft() + p);
|
||||
}
|
||||
|
||||
void CIntObject::addUsedEvents(ui16 newActions)
|
||||
{
|
||||
if (active_m)
|
||||
activate(~used & newActions);
|
||||
if (isActive())
|
||||
activateEvents(~used & newActions);
|
||||
used |= newActions;
|
||||
}
|
||||
|
||||
void CIntObject::removeUsedEvents(ui16 newActions)
|
||||
{
|
||||
if (active_m)
|
||||
deactivate(used & newActions);
|
||||
if (isActive())
|
||||
deactivateEvents(used & newActions);
|
||||
used &= ~newActions;
|
||||
}
|
||||
|
||||
void CIntObject::disable()
|
||||
{
|
||||
if(active)
|
||||
if(isActive())
|
||||
deactivate();
|
||||
|
||||
recActions = DISPOSE;
|
||||
@ -189,7 +134,7 @@ void CIntObject::disable()
|
||||
|
||||
void CIntObject::enable()
|
||||
{
|
||||
if(!active_m && (!parent_m || parent_m->active))
|
||||
if(!isActive() && (!parent_m || parent_m->isActive()))
|
||||
{
|
||||
activate();
|
||||
redraw();
|
||||
@ -246,9 +191,9 @@ void CIntObject::addChild(CIntObject * child, bool adjustPosition)
|
||||
if(adjustPosition)
|
||||
child->moveBy(pos.topLeft(), adjustPosition);
|
||||
|
||||
if (!active && child->active)
|
||||
if (!isActive() && child->isActive())
|
||||
child->deactivate();
|
||||
if (active && !child->active)
|
||||
if (isActive()&& !child->isActive())
|
||||
child->activate();
|
||||
}
|
||||
|
||||
@ -273,7 +218,7 @@ void CIntObject::redraw()
|
||||
{
|
||||
//currently most of calls come from active objects so this check won't affect them
|
||||
//it should fix glitches when called by inactive elements located below active window
|
||||
if (active)
|
||||
if (isActive())
|
||||
{
|
||||
if (parent_m && (type & REDRAW_PARENT))
|
||||
{
|
||||
@ -288,6 +233,11 @@ void CIntObject::redraw()
|
||||
}
|
||||
}
|
||||
|
||||
bool CIntObject::isInside(const Point & position)
|
||||
{
|
||||
return pos.isInside(position);
|
||||
}
|
||||
|
||||
void CIntObject::onScreenResize()
|
||||
{
|
||||
center(pos, true);
|
||||
@ -320,6 +270,7 @@ bool CIntObject::captureThisKey(EShortcut key)
|
||||
|
||||
CKeyShortcut::CKeyShortcut()
|
||||
: assignedKey(EShortcut::NONE)
|
||||
, shortcutPressed(false)
|
||||
{}
|
||||
|
||||
CKeyShortcut::CKeyShortcut(EShortcut key)
|
||||
@ -329,23 +280,19 @@ CKeyShortcut::CKeyShortcut(EShortcut key)
|
||||
|
||||
void CKeyShortcut::keyPressed(EShortcut key)
|
||||
{
|
||||
if( assignedKey == key && assignedKey != EShortcut::NONE)
|
||||
if( assignedKey == key && assignedKey != EShortcut::NONE && !shortcutPressed)
|
||||
{
|
||||
bool prev = mouseState(MouseButton::LEFT);
|
||||
updateMouseState(MouseButton::LEFT, true);
|
||||
clickLeft(true, prev);
|
||||
|
||||
shortcutPressed = true;
|
||||
clickLeft(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyShortcut::keyReleased(EShortcut key)
|
||||
{
|
||||
if( assignedKey == key && assignedKey != EShortcut::NONE)
|
||||
if( assignedKey == key && assignedKey != EShortcut::NONE && shortcutPressed)
|
||||
{
|
||||
bool prev = mouseState(MouseButton::LEFT);
|
||||
updateMouseState(MouseButton::LEFT, false);
|
||||
clickLeft(false, prev);
|
||||
|
||||
shortcutPressed = false;
|
||||
clickLeft(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,6 +308,3 @@ void WindowBase::close()
|
||||
logGlobal->error("Only top interface must be closed");
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
|
||||
IStatusBar::~IStatusBar()
|
||||
{}
|
||||
|
@ -9,84 +9,50 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "MouseButton.h"
|
||||
#include "../render/Graphics.h"
|
||||
#include "../../lib/Rect.h"
|
||||
#include "EventsReceiver.h"
|
||||
|
||||
struct SDL_Surface;
|
||||
class CGuiHandler;
|
||||
class CPicture;
|
||||
enum class EShortcut;
|
||||
|
||||
using boost::logic::tribool;
|
||||
|
||||
// Defines a activate/deactive method
|
||||
class IActivatable
|
||||
{
|
||||
public:
|
||||
virtual void activate()=0;
|
||||
virtual void deactivate()=0;
|
||||
virtual ~IActivatable(){};
|
||||
};
|
||||
|
||||
class IUpdateable
|
||||
{
|
||||
public:
|
||||
virtual void update()=0;
|
||||
virtual ~IUpdateable(){};
|
||||
virtual ~IUpdateable() = default;
|
||||
};
|
||||
|
||||
// Defines a show method
|
||||
class IShowable
|
||||
class IShowActivatable
|
||||
{
|
||||
public:
|
||||
virtual void activate()=0;
|
||||
virtual void deactivate()=0;
|
||||
|
||||
virtual void redraw()=0;
|
||||
virtual void show(SDL_Surface * to) = 0;
|
||||
virtual void showAll(SDL_Surface * to)
|
||||
{
|
||||
show(to);
|
||||
}
|
||||
virtual ~IShowable(){};
|
||||
virtual void showAll(SDL_Surface * to) = 0;
|
||||
|
||||
virtual void onScreenResize() = 0;
|
||||
virtual ~IShowActivatable() = default;
|
||||
};
|
||||
|
||||
class IShowActivatable : public IShowable, public IActivatable
|
||||
// Base UI element
|
||||
class CIntObject : public IShowActivatable, public AEventsReceiver //interface object
|
||||
{
|
||||
ui16 used;
|
||||
|
||||
//non-const versions of fields to allow changing them in CIntObject
|
||||
CIntObject *parent_m; //parent object
|
||||
|
||||
public:
|
||||
//redraw parent flag - this int may be semi-transparent and require redraw of parent window
|
||||
enum {REDRAW_PARENT=8};
|
||||
int type; //bin flags using etype
|
||||
IShowActivatable();
|
||||
virtual ~IShowActivatable(){};
|
||||
};
|
||||
|
||||
// Base UI element
|
||||
class CIntObject : public IShowActivatable //interface object
|
||||
{
|
||||
ui16 used;//change via addUsed() or delUsed
|
||||
|
||||
std::map<MouseButton, bool> currentMouseState;
|
||||
|
||||
//non-const versions of fields to allow changing them in CIntObject
|
||||
CIntObject *parent_m; //parent object
|
||||
ui16 active_m;
|
||||
|
||||
protected:
|
||||
//activate or deactivate specific action (LCLICK, RCLICK...)
|
||||
void activate(ui16 what);
|
||||
void deactivate(ui16 what);
|
||||
|
||||
public:
|
||||
/*
|
||||
* Functions and fields that supposed to be private but are not for now.
|
||||
* Don't use them unless you really know what they are for
|
||||
*/
|
||||
std::vector<CIntObject *> children;
|
||||
|
||||
|
||||
/*
|
||||
* Public interface
|
||||
*/
|
||||
|
||||
/// read-only parent access. May not be a "clean" solution but allows some compatibility
|
||||
CIntObject * const & parent;
|
||||
|
||||
@ -96,43 +62,14 @@ public:
|
||||
CIntObject(int used=0, Point offset=Point());
|
||||
virtual ~CIntObject();
|
||||
|
||||
void updateMouseState(MouseButton btn, bool state) { currentMouseState[btn] = state; }
|
||||
bool mouseState(MouseButton btn) const { return currentMouseState.count(btn) ? currentMouseState.at(btn) : false; }
|
||||
|
||||
virtual void click(MouseButton btn, tribool down, bool previousState);
|
||||
virtual void clickLeft(tribool down, bool previousState) {}
|
||||
virtual void clickRight(tribool down, bool previousState) {}
|
||||
virtual void clickMiddle(tribool down, bool previousState) {}
|
||||
|
||||
//hover handling
|
||||
/*const*/ bool hovered; //for determining if object is hovered
|
||||
virtual void hover (bool on){}
|
||||
void hover (bool on) override{}
|
||||
|
||||
//keyboard handling
|
||||
bool captureAllKeys; //if true, only this object should get info about pressed keys
|
||||
virtual void keyPressed(EShortcut key){}
|
||||
virtual void keyReleased(EShortcut key){}
|
||||
virtual bool captureThisKey(EShortcut key); //allows refining captureAllKeys against specific events (eg. don't capture ENTER)
|
||||
|
||||
virtual void textInputed(const std::string & enteredText){};
|
||||
virtual void textEdited(const std::string & enteredText){};
|
||||
bool captureThisKey(EShortcut key) override; //allows refining captureAllKeys against specific events (eg. don't capture ENTER)
|
||||
|
||||
//mouse movement handling
|
||||
bool strongInterest; //if true - report all mouse movements, if not - only when hovered
|
||||
virtual void mouseMoved (const Point & cursorPosition){}
|
||||
|
||||
//time handling
|
||||
virtual void tick(uint32_t msPassed){}
|
||||
|
||||
//mouse wheel
|
||||
virtual void wheelScrolled(bool down, bool in){}
|
||||
|
||||
//double click
|
||||
virtual void onDoubleClick(){}
|
||||
|
||||
// These are the arguments that can be used to determine what kind of input the CIntObject will receive
|
||||
enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, MCLICK=1024, ALL=0xffff};
|
||||
const ui16 & active;
|
||||
void addUsedEvents(ui16 newActions);
|
||||
void removeUsedEvents(ui16 newActions);
|
||||
|
||||
@ -147,7 +84,6 @@ public:
|
||||
/// deactivates or activates UI element based on flag
|
||||
void setEnabled(bool on);
|
||||
|
||||
|
||||
// activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse)
|
||||
// usually used automatically by parent
|
||||
void activate() override;
|
||||
@ -162,7 +98,9 @@ public:
|
||||
|
||||
/// called only for windows whenever screen size changes
|
||||
/// default behavior is to re-center, can be overriden
|
||||
virtual void onScreenResize();
|
||||
void onScreenResize() override;
|
||||
|
||||
bool isInside(const Point & position) override;
|
||||
|
||||
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
|
||||
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
|
||||
@ -173,26 +111,18 @@ public:
|
||||
|
||||
void addChild(CIntObject *child, bool adjustPosition = false);
|
||||
void removeChild(CIntObject *child, bool adjustPosition = false);
|
||||
//delChild - not needed, use normal "delete child" instead
|
||||
//delChildNull - not needed, use "vstd::clear_pointer(child)" instead
|
||||
|
||||
/*
|
||||
* Functions that should be used only by specific GUI elements. Don't use them unless you really know why they are here
|
||||
*/
|
||||
|
||||
//functions for printing text. Use CLabel where possible instead
|
||||
void printAtLoc(const std::string & text, int x, int y, EFonts font, SDL_Color color, SDL_Surface * dst);
|
||||
void printAtMiddleLoc(const std::string & text, int x, int y, EFonts font, SDL_Color color, SDL_Surface * dst);
|
||||
void printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color color, SDL_Surface * dst);
|
||||
void printAtMiddleWBLoc(const std::string & text, int x, int y, EFonts font, int charsPerLine, SDL_Color color, SDL_Surface * dst);
|
||||
|
||||
friend class CGuiHandler;
|
||||
/// functions for printing text.
|
||||
/// Deprecated. Use CLabel where possible instead
|
||||
void printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, const SDL_Color & color, SDL_Surface * dst);
|
||||
void printAtMiddleWBLoc(const std::string & text, const Point &p, EFonts font, int charsPerLine, const SDL_Color & color, SDL_Surface * dst);
|
||||
};
|
||||
|
||||
/// Class for binding keys to left mouse button clicks
|
||||
/// Classes wanting use it should have it as one of their base classes
|
||||
class CKeyShortcut : public virtual CIntObject
|
||||
{
|
||||
bool shortcutPressed;
|
||||
public:
|
||||
EShortcut assignedKey;
|
||||
CKeyShortcut();
|
||||
@ -213,7 +143,7 @@ protected:
|
||||
class IStatusBar
|
||||
{
|
||||
public:
|
||||
virtual ~IStatusBar();
|
||||
virtual ~IStatusBar() = default;
|
||||
|
||||
/// set current text for the status bar
|
||||
virtual void write(const std::string & text) = 0;
|
||||
|
@ -251,7 +251,7 @@ void CursorHandler::updateSpellcastCursor()
|
||||
{
|
||||
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
|
||||
|
||||
frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.f;
|
||||
frameTime += GH.framerate().getElapsedMilliseconds() / 1000.f;
|
||||
size_t newFrame = frame;
|
||||
|
||||
while (frameTime >= frameDisplayDuration)
|
||||
|
244
client/gui/EventDispatcher.cpp
Normal file
244
client/gui/EventDispatcher.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* EventDispatcher.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "EventDispatcher.h"
|
||||
|
||||
#include "EventsReceiver.h"
|
||||
#include "FramerateManager.h"
|
||||
#include "CGuiHandler.h"
|
||||
#include "MouseButton.h"
|
||||
|
||||
#include "../../lib/Point.h"
|
||||
|
||||
template<typename Functor>
|
||||
void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
|
||||
{
|
||||
auto processList = [&](ui16 mask, EventReceiversList & lst)
|
||||
{
|
||||
if(mask & activityFlag)
|
||||
cb(lst);
|
||||
};
|
||||
|
||||
processList(AEventsReceiver::LCLICK, lclickable);
|
||||
processList(AEventsReceiver::RCLICK, rclickable);
|
||||
processList(AEventsReceiver::MCLICK, mclickable);
|
||||
processList(AEventsReceiver::HOVER, hoverable);
|
||||
processList(AEventsReceiver::MOVE, motioninterested);
|
||||
processList(AEventsReceiver::KEYBOARD, keyinterested);
|
||||
processList(AEventsReceiver::TIME, timeinterested);
|
||||
processList(AEventsReceiver::WHEEL, wheelInterested);
|
||||
processList(AEventsReceiver::DOUBLECLICK, doubleClickInterested);
|
||||
processList(AEventsReceiver::TEXTINPUT, textInterested);
|
||||
}
|
||||
|
||||
void EventDispatcher::activateElement(AEventsReceiver * elem, ui16 activityFlag)
|
||||
{
|
||||
processLists(activityFlag,[&](EventReceiversList & lst){
|
||||
lst.push_front(elem);
|
||||
});
|
||||
elem->activeState |= activityFlag;
|
||||
}
|
||||
|
||||
void EventDispatcher::deactivateElement(AEventsReceiver * elem, ui16 activityFlag)
|
||||
{
|
||||
processLists(activityFlag,[&](EventReceiversList & lst){
|
||||
auto hlp = std::find(lst.begin(),lst.end(),elem);
|
||||
assert(hlp != lst.end());
|
||||
lst.erase(hlp);
|
||||
});
|
||||
elem->activeState &= ~activityFlag;
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchTimer(uint32_t msPassed)
|
||||
{
|
||||
EventReceiversList hlp = timeinterested;
|
||||
for (auto & elem : hlp)
|
||||
{
|
||||
if(!vstd::contains(timeinterested,elem)) continue;
|
||||
(elem)->tick(msPassed);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchShortcutPressed(const std::vector<EShortcut> & shortcutsVector)
|
||||
{
|
||||
bool keysCaptured = false;
|
||||
|
||||
for(auto & i : keyinterested)
|
||||
for(EShortcut shortcut : shortcutsVector)
|
||||
if(i->captureThisKey(shortcut))
|
||||
keysCaptured = true;
|
||||
|
||||
EventReceiversList miCopy = keyinterested;
|
||||
|
||||
for(auto & i : miCopy)
|
||||
{
|
||||
for(EShortcut shortcut : shortcutsVector)
|
||||
if(vstd::contains(keyinterested, i) && (!keysCaptured || i->captureThisKey(shortcut)))
|
||||
{
|
||||
i->keyPressed(shortcut);
|
||||
if (keysCaptured)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchShortcutReleased(const std::vector<EShortcut> & shortcutsVector)
|
||||
{
|
||||
bool keysCaptured = false;
|
||||
|
||||
for(auto & i : keyinterested)
|
||||
for(EShortcut shortcut : shortcutsVector)
|
||||
if(i->captureThisKey(shortcut))
|
||||
keysCaptured = true;
|
||||
|
||||
EventReceiversList miCopy = keyinterested;
|
||||
|
||||
for(auto & i : miCopy)
|
||||
{
|
||||
for(EShortcut shortcut : shortcutsVector)
|
||||
if(vstd::contains(keyinterested, i) && (!keysCaptured || i->captureThisKey(shortcut)))
|
||||
{
|
||||
i->keyReleased(shortcut);
|
||||
if (keysCaptured)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventDispatcher::EventReceiversList & EventDispatcher::getListForMouseButton(MouseButton button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case MouseButton::LEFT:
|
||||
return lclickable;
|
||||
case MouseButton::RIGHT:
|
||||
return rclickable;
|
||||
case MouseButton::MIDDLE:
|
||||
return mclickable;
|
||||
}
|
||||
throw std::runtime_error("Invalid mouse button in getListForMouseButton");
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
|
||||
{
|
||||
bool doubleClicked = false;
|
||||
auto hlp = doubleClickInterested;
|
||||
|
||||
for(auto & i : hlp)
|
||||
{
|
||||
if(!vstd::contains(doubleClickInterested, i))
|
||||
continue;
|
||||
|
||||
if(i->isInside(position))
|
||||
{
|
||||
i->onDoubleClick();
|
||||
doubleClicked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!doubleClicked)
|
||||
dispatchMouseButtonPressed(MouseButton::LEFT, position);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchMouseButtonPressed(const MouseButton & button, const Point & position)
|
||||
{
|
||||
handleMouseButtonClick(getListForMouseButton(button), button, true);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchMouseButtonReleased(const MouseButton & button, const Point & position)
|
||||
{
|
||||
handleMouseButtonClick(getListForMouseButton(button), button, false);
|
||||
}
|
||||
|
||||
void EventDispatcher::handleMouseButtonClick(EventReceiversList & interestedObjs, MouseButton btn, bool isPressed)
|
||||
{
|
||||
auto hlp = interestedObjs;
|
||||
for(auto & i : hlp)
|
||||
{
|
||||
if(!vstd::contains(interestedObjs, i))
|
||||
continue;
|
||||
|
||||
auto prev = i->isMouseButtonPressed(btn);
|
||||
if(!isPressed)
|
||||
i->currentMouseState[btn] = isPressed;
|
||||
if(i->isInside(GH.getCursorPosition()))
|
||||
{
|
||||
if(isPressed)
|
||||
i->currentMouseState[btn] = isPressed;
|
||||
i->click(btn, isPressed, prev);
|
||||
}
|
||||
else if(!isPressed)
|
||||
i->click(btn, boost::logic::indeterminate, prev);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchMouseScrolled(const Point & distance, const Point & position)
|
||||
{
|
||||
EventReceiversList hlp = wheelInterested;
|
||||
for(auto & i : hlp)
|
||||
{
|
||||
if(!vstd::contains(wheelInterested,i))
|
||||
continue;
|
||||
i->wheelScrolled(distance.y < 0, i->isInside(position));
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchTextInput(const std::string & text)
|
||||
{
|
||||
for(auto it : textInterested)
|
||||
{
|
||||
it->textInputed(text);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchTextEditing(const std::string & text)
|
||||
{
|
||||
for(auto it : textInterested)
|
||||
{
|
||||
it->textEdited(text);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchMouseMoved(const Point & position)
|
||||
{
|
||||
//sending active, hovered hoverable objects hover() call
|
||||
EventReceiversList hlp;
|
||||
|
||||
auto hoverableCopy = hoverable;
|
||||
for(auto & elem : hoverableCopy)
|
||||
{
|
||||
if(elem->isInside(GH.getCursorPosition()))
|
||||
{
|
||||
if (!(elem)->isHovered())
|
||||
hlp.push_back((elem));
|
||||
}
|
||||
else if ((elem)->isHovered())
|
||||
{
|
||||
(elem)->hover(false);
|
||||
(elem)->hoveredState = false;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto & elem : hlp)
|
||||
{
|
||||
elem->hover(true);
|
||||
elem->hoveredState = true;
|
||||
}
|
||||
|
||||
//sending active, MotionInterested objects mouseMoved() call
|
||||
EventReceiversList miCopy = motioninterested;
|
||||
for(auto & elem : miCopy)
|
||||
{
|
||||
if(elem->strongInterestState || elem->isInside(position)) //checking bounds including border fixes bug #2476
|
||||
{
|
||||
(elem)->mouseMoved(position);
|
||||
}
|
||||
}
|
||||
}
|
68
client/gui/EventDispatcher.h
Normal file
68
client/gui/EventDispatcher.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* EventDispatcher.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class Point;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class AEventsReceiver;
|
||||
enum class MouseButton;
|
||||
enum class EShortcut;
|
||||
|
||||
/// Class that receives events from event producers and dispatches it to UI elements that are interested in this event
|
||||
class EventDispatcher
|
||||
{
|
||||
using EventReceiversList = std::list<AEventsReceiver *>;
|
||||
|
||||
/// list of UI elements that are interested in particular event
|
||||
EventReceiversList lclickable;
|
||||
EventReceiversList rclickable;
|
||||
EventReceiversList mclickable;
|
||||
EventReceiversList hoverable;
|
||||
EventReceiversList keyinterested;
|
||||
EventReceiversList motioninterested;
|
||||
EventReceiversList timeinterested;
|
||||
EventReceiversList wheelInterested;
|
||||
EventReceiversList doubleClickInterested;
|
||||
EventReceiversList textInterested;
|
||||
|
||||
EventReceiversList & getListForMouseButton(MouseButton button);
|
||||
|
||||
void handleMouseButtonClick(EventReceiversList & interestedObjs, MouseButton btn, bool isPressed);
|
||||
|
||||
template<typename Functor>
|
||||
void processLists(ui16 activityFlag, const Functor & cb);
|
||||
|
||||
public:
|
||||
/// add specified UI element as interested. Uses unnamed enum from AEventsReceiver for activity flags
|
||||
void activateElement(AEventsReceiver * elem, ui16 activityFlag);
|
||||
|
||||
/// removes specified UI element as interested for specified activities
|
||||
void deactivateElement(AEventsReceiver * elem, ui16 activityFlag);
|
||||
|
||||
/// Regular timer event
|
||||
void dispatchTimer(uint32_t msPassed);
|
||||
|
||||
/// Shortcut events (e.g. keyboard keys)
|
||||
void dispatchShortcutPressed(const std::vector<EShortcut> & shortcuts);
|
||||
void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
|
||||
|
||||
/// Mouse events
|
||||
void dispatchMouseButtonPressed(const MouseButton & button, const Point & position);
|
||||
void dispatchMouseButtonReleased(const MouseButton & button, const Point & position);
|
||||
void dispatchMouseScrolled(const Point & distance, const Point & position);
|
||||
void dispatchMouseDoubleClick(const Point & position);
|
||||
void dispatchMouseMoved(const Point & position);
|
||||
|
||||
/// Text input events
|
||||
void dispatchTextInput(const std::string & text);
|
||||
void dispatchTextEditing(const std::string & text);
|
||||
};
|
74
client/gui/EventsReceiver.cpp
Normal file
74
client/gui/EventsReceiver.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* EventsReceiver.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "EventsReceiver.h"
|
||||
|
||||
#include "MouseButton.h"
|
||||
#include "CGuiHandler.h"
|
||||
#include "EventDispatcher.h"
|
||||
|
||||
AEventsReceiver::AEventsReceiver()
|
||||
: activeState(0)
|
||||
, hoveredState(false)
|
||||
, strongInterestState(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool AEventsReceiver::isHovered() const
|
||||
{
|
||||
return hoveredState;
|
||||
}
|
||||
|
||||
bool AEventsReceiver::isActive() const
|
||||
{
|
||||
return activeState;
|
||||
}
|
||||
|
||||
bool AEventsReceiver::isMouseButtonPressed(MouseButton btn) const
|
||||
{
|
||||
return currentMouseState.count(btn) ? currentMouseState.at(btn) : false;
|
||||
}
|
||||
|
||||
void AEventsReceiver::setMoveEventStrongInterest(bool on)
|
||||
{
|
||||
strongInterestState = on;
|
||||
}
|
||||
|
||||
void AEventsReceiver::activateEvents(ui16 what)
|
||||
{
|
||||
assert((what & GENERAL) || (activeState & GENERAL));
|
||||
|
||||
activeState |= GENERAL;
|
||||
GH.events().activateElement(this, what);
|
||||
}
|
||||
|
||||
void AEventsReceiver::deactivateEvents(ui16 what)
|
||||
{
|
||||
if (what & GENERAL)
|
||||
activeState &= ~GENERAL;
|
||||
GH.events().deactivateElement(this, what & activeState);
|
||||
}
|
||||
|
||||
void AEventsReceiver::click(MouseButton btn, tribool down, bool previousState)
|
||||
{
|
||||
switch(btn)
|
||||
{
|
||||
default:
|
||||
case MouseButton::LEFT:
|
||||
clickLeft(down, previousState);
|
||||
break;
|
||||
case MouseButton::MIDDLE:
|
||||
clickMiddle(down, previousState);
|
||||
break;
|
||||
case MouseButton::RIGHT:
|
||||
clickRight(down, previousState);
|
||||
break;
|
||||
}
|
||||
}
|
77
client/gui/EventsReceiver.h
Normal file
77
client/gui/EventsReceiver.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* EventsReceiver.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class Point;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class EventDispatcher;
|
||||
enum class MouseButton;
|
||||
enum class EShortcut;
|
||||
using boost::logic::tribool;
|
||||
|
||||
/// Class that is capable of subscribing and receiving input events
|
||||
/// Acts as base class for all UI elements
|
||||
class AEventsReceiver
|
||||
{
|
||||
friend class EventDispatcher;
|
||||
|
||||
ui16 activeState;
|
||||
bool hoveredState;
|
||||
bool strongInterestState;
|
||||
std::map<MouseButton, bool> currentMouseState;
|
||||
|
||||
void click(MouseButton btn, tribool down, bool previousState);
|
||||
protected:
|
||||
|
||||
/// If set, UI element will receive all mouse movement events, even those outside this element
|
||||
void setMoveEventStrongInterest(bool on);
|
||||
|
||||
/// Activates particular events for this UI element. Uses unnamed enum from this class
|
||||
void activateEvents(ui16 what);
|
||||
/// Deactivates particular events for this UI element. Uses unnamed enum from this class
|
||||
void deactivateEvents(ui16 what);
|
||||
|
||||
virtual void clickLeft(tribool down, bool previousState) {}
|
||||
virtual void clickRight(tribool down, bool previousState) {}
|
||||
virtual void clickMiddle(tribool down, bool previousState) {}
|
||||
|
||||
virtual void textInputed(const std::string & enteredText) {}
|
||||
virtual void textEdited(const std::string & enteredText) {}
|
||||
|
||||
virtual void tick(uint32_t msPassed) {}
|
||||
virtual void wheelScrolled(bool down, bool in) {}
|
||||
virtual void mouseMoved(const Point & cursorPosition) {}
|
||||
virtual void hover(bool on) {}
|
||||
virtual void onDoubleClick() {}
|
||||
|
||||
virtual void keyPressed(EShortcut key) {}
|
||||
virtual void keyReleased(EShortcut key) {}
|
||||
|
||||
virtual bool captureThisKey(EShortcut key) = 0;
|
||||
virtual bool isInside(const Point & position) = 0;
|
||||
|
||||
public:
|
||||
AEventsReceiver();
|
||||
virtual ~AEventsReceiver() = default;
|
||||
|
||||
/// These are the arguments that can be used to determine what kind of input UI element will receive
|
||||
enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, MCLICK=1024, ALL=0xffff};
|
||||
|
||||
/// Returns true if element is currently hovered by mouse
|
||||
bool isHovered() const;
|
||||
|
||||
/// Returns true if element is currently active and may receive events
|
||||
bool isActive() const;
|
||||
|
||||
/// Returns true if particular mouse button was pressed when inside this element
|
||||
bool isMouseButtonPressed(MouseButton btn) const;
|
||||
};
|
@ -258,7 +258,7 @@ EShortcut InterfaceObjectConfigurable::readHotkey(const JsonNode & config) const
|
||||
return EShortcut::NONE;
|
||||
}
|
||||
|
||||
EShortcut result = GH.shortcutsHandler().findShortcut(config.String());
|
||||
EShortcut result = GH.shortcuts().findShortcut(config.String());
|
||||
if (result == EShortcut::NONE)
|
||||
logGlobal->error("Invalid hotkey '%s' in interface configuration!", config.String());
|
||||
return result;;
|
||||
|
@ -109,12 +109,8 @@ void WindowHandler::simpleRedraw()
|
||||
void WindowHandler::onScreenResize()
|
||||
{
|
||||
for(const auto & entry : windowsStack)
|
||||
{
|
||||
auto intObject = std::dynamic_pointer_cast<CIntObject>(entry);
|
||||
entry->onScreenResize();
|
||||
|
||||
if(intObject)
|
||||
intObject->onScreenResize();
|
||||
}
|
||||
totalRedraw();
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ CSelectionBase::CSelectionBase(ESelectionScreen type)
|
||||
|
||||
void CSelectionBase::toggleTab(std::shared_ptr<CIntObject> tab)
|
||||
{
|
||||
if(curTab && curTab->active)
|
||||
if(curTab && curTab->isActive())
|
||||
{
|
||||
curTab->deactivate();
|
||||
curTab->recActions = 0;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
@ -397,21 +398,16 @@ void TemplatesDropBox::ListItem::hover(bool on)
|
||||
if(h && w)
|
||||
{
|
||||
if(w->getText().empty())
|
||||
{
|
||||
hovered = false;
|
||||
h->visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
h->visible = on;
|
||||
}
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
void TemplatesDropBox::ListItem::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(down && hovered)
|
||||
if(down && isHovered())
|
||||
{
|
||||
dropBox.setTemplate(item);
|
||||
}
|
||||
@ -469,19 +465,14 @@ void TemplatesDropBox::sliderMove(int slidPos)
|
||||
redraw();
|
||||
}
|
||||
|
||||
void TemplatesDropBox::hover(bool on)
|
||||
{
|
||||
hovered = on;
|
||||
}
|
||||
|
||||
void TemplatesDropBox::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(down && !hovered)
|
||||
if(down && !isActive())
|
||||
{
|
||||
auto w = widget<CSlider>("slider");
|
||||
|
||||
// pop the interface only if the mouse is not clicking on the slider
|
||||
if (!w || !w->mouseState(MouseButton::LEFT))
|
||||
if (!w || !w->isMouseButtonPressed(MouseButton::LEFT))
|
||||
{
|
||||
assert(GH.windows().isTopWindow(this));
|
||||
GH.windows().popWindows(1);
|
||||
|
@ -70,7 +70,6 @@ class TemplatesDropBox : public InterfaceObjectConfigurable
|
||||
public:
|
||||
TemplatesDropBox(RandomMapTab & randomMapTab, int3 size);
|
||||
|
||||
void hover(bool on) override;
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void setTemplate(const CRmgTemplate *);
|
||||
|
||||
|
@ -277,7 +277,7 @@ void SelectionTab::clickLeft(tribool down, bool previousState)
|
||||
}
|
||||
#ifdef VCMI_IOS
|
||||
// focus input field if clicked inside it
|
||||
else if(inputName && inputName->active && inputNameRect.isInside(GH.getCursorPosition()))
|
||||
else if(inputName && inputName->isActive() && inputNameRect.isInside(GH.getCursorPosition()))
|
||||
inputName->giveFocus();
|
||||
#endif
|
||||
}
|
||||
@ -408,7 +408,7 @@ void SelectionTab::select(int position)
|
||||
|
||||
rememberCurrentSelection();
|
||||
|
||||
if(inputName && inputName->active)
|
||||
if(inputName && inputName->isActive())
|
||||
{
|
||||
auto filename = *CResourceHandler::get("local")->getResourceName(ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));
|
||||
inputName->setText(filename.stem().string());
|
||||
|
@ -119,7 +119,7 @@ void CCampaignScreen::CCampaignButton::show(SDL_Surface * to)
|
||||
CIntObject::show(to);
|
||||
|
||||
// Play the campaign button video when the mouse cursor is placed over the button
|
||||
if(hovered)
|
||||
if(isHovered())
|
||||
{
|
||||
if(CCS->videoh->fname != video)
|
||||
CCS->videoh->open(video);
|
||||
|
@ -233,7 +233,7 @@ std::shared_ptr<CButton> CMenuEntry::createButton(CMenuScreen * parent, const Js
|
||||
if(posy < 0)
|
||||
posy = pos.h + posy;
|
||||
|
||||
EShortcut shortcut = GH.shortcutsHandler().findShortcut(button["shortcut"].String());
|
||||
EShortcut shortcut = GH.shortcuts().findShortcut(button["shortcut"].String());
|
||||
|
||||
auto result = std::make_shared<CButton>(Point(posx, posy), button["name"].String(), help, command, shortcut);
|
||||
|
||||
@ -333,7 +333,6 @@ void CMainMenu::update()
|
||||
}
|
||||
|
||||
// Handles mouse and key input
|
||||
GH.updateTime();
|
||||
GH.handleEvents();
|
||||
|
||||
// check for null othervice crash on finishing a campaign
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
@ -105,8 +106,8 @@ void MapViewActions::handleSwipeMove(const Point & cursorPosition)
|
||||
if(!swipeEnabled() && !GH.isMouseButtonPressed(MouseButton::MIDDLE))
|
||||
return;
|
||||
|
||||
// on mobile platforms with enabled swipe any button is enough
|
||||
if(swipeEnabled() && (!GH.isMouseButtonPressed() || GH.multifinger))
|
||||
// on mobile platforms with enabled swipe we use left button
|
||||
if(swipeEnabled() && !GH.isMouseButtonPressed(MouseButton::LEFT))
|
||||
return;
|
||||
|
||||
if(!isSwiping)
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/NotificationHandler.h"
|
||||
#include "../eventsSDL/NotificationHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "CMT.h"
|
||||
#include "SDL_Extensions.h"
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "../battle/BattleInterface.h"
|
||||
#include "../battle/BattleInterfaceClasses.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/MouseButton.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../render/CAnimation.h"
|
||||
@ -54,7 +55,7 @@ void CButton::update()
|
||||
newPos = (int)image->size()-1;
|
||||
image->setFrame(newPos);
|
||||
|
||||
if (active)
|
||||
if (isActive())
|
||||
redraw();
|
||||
}
|
||||
|
||||
@ -173,22 +174,28 @@ void CButton::clickLeft(tribool down, bool previousState)
|
||||
|
||||
if (down)
|
||||
{
|
||||
if (!soundDisabled)
|
||||
CCS->soundh->playSound(soundBase::button);
|
||||
setState(PRESSED);
|
||||
}
|
||||
else if(hoverable && hovered)
|
||||
setState(HIGHLIGHTED);
|
||||
else
|
||||
setState(NORMAL);
|
||||
if (getState() != PRESSED)
|
||||
{
|
||||
if (!soundDisabled)
|
||||
CCS->soundh->playSound(soundBase::button);
|
||||
setState(PRESSED);
|
||||
|
||||
if (actOnDown && down)
|
||||
{
|
||||
onButtonClicked();
|
||||
if (actOnDown)
|
||||
onButtonClicked();
|
||||
}
|
||||
}
|
||||
else if (!actOnDown && previousState && (down==false))
|
||||
else
|
||||
{
|
||||
onButtonClicked();
|
||||
if (getState() == PRESSED)
|
||||
{
|
||||
if(hoverable && isHovered())
|
||||
setState(HIGHLIGHTED);
|
||||
else
|
||||
setState(NORMAL);
|
||||
|
||||
if (!actOnDown && previousState && (down == false))
|
||||
onButtonClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,7 +499,7 @@ void CVolumeSlider::moveTo(int id)
|
||||
vstd::abetween<int>(id, 0, animImage->size() - 1);
|
||||
animImage->setFrame(id);
|
||||
animImage->moveTo(Point(pos.x + (animImage->pos.w + 1) * id, pos.y));
|
||||
if (active)
|
||||
if (isActive())
|
||||
redraw();
|
||||
}
|
||||
|
||||
@ -550,8 +557,7 @@ void CVolumeSlider::wheelScrolled(bool down, bool in)
|
||||
|
||||
void CSlider::sliderClicked()
|
||||
{
|
||||
if(!(active & MOVE))
|
||||
addUsedEvents(MOVE);
|
||||
addUsedEvents(MOVE);
|
||||
}
|
||||
|
||||
void CSlider::mouseMoved (const Point & cursorPosition)
|
||||
@ -688,12 +694,11 @@ void CSlider::clickLeft(tribool down, bool previousState)
|
||||
return;
|
||||
// if (rw>1) return;
|
||||
// if (rw<0) return;
|
||||
slider->clickLeft(true, slider->mouseState(MouseButton::LEFT));
|
||||
slider->clickLeft(true, slider->isMouseButtonPressed(MouseButton::LEFT));
|
||||
moveTo((int)(rw * positions + 0.5));
|
||||
return;
|
||||
}
|
||||
if(active & MOVE)
|
||||
removeUsedEvents(MOVE);
|
||||
removeUsedEvents(MOVE);
|
||||
}
|
||||
|
||||
CSlider::CSlider(Point position, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, CSlider::EStyle style)
|
||||
@ -710,7 +715,7 @@ CSlider::CSlider(Point position, int totalw, std::function<void(int)> Moved, int
|
||||
vstd::amax(value, 0);
|
||||
vstd::amin(value, positions);
|
||||
|
||||
strongInterest = true;
|
||||
setMoveEventStrongInterest(true);
|
||||
|
||||
pos.x += position.x;
|
||||
pos.y += position.y;
|
||||
|
@ -213,7 +213,7 @@ void CHeroArtPlace::showAll(SDL_Surface* to)
|
||||
CIntObject::showAll(to);
|
||||
}
|
||||
|
||||
if(marked && active)
|
||||
if(marked && isActive())
|
||||
{
|
||||
// Draw vertical bars.
|
||||
for(int i = 0; i < pos.h; ++i)
|
||||
|
@ -169,7 +169,7 @@ void CArtifactsOfHeroBase::scrollBackpackForArtSet(int offset, const CArtifactSe
|
||||
|
||||
void CArtifactsOfHeroBase::safeRedraw()
|
||||
{
|
||||
if(active)
|
||||
if(isActive())
|
||||
{
|
||||
if(parent)
|
||||
parent->redraw();
|
||||
|
@ -238,7 +238,7 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const
|
||||
if(artSetPtr)
|
||||
{
|
||||
const auto hero = artSetPtr->getHero();
|
||||
if(artSetPtr->active)
|
||||
if(artSetPtr->isActive())
|
||||
{
|
||||
if(pickedArtInst)
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ std::shared_ptr<CIntObject> CObjectList::createItem(size_t index)
|
||||
|
||||
item->recActions = defActions;
|
||||
addChild(item.get());
|
||||
if (active)
|
||||
if (isActive())
|
||||
item->activate();
|
||||
return item;
|
||||
}
|
||||
@ -70,7 +70,7 @@ void CTabbedInt::reset()
|
||||
activeTab = createItem(activeID);
|
||||
activeTab->moveTo(pos.topLeft());
|
||||
|
||||
if(active)
|
||||
if(isActive())
|
||||
redraw();
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ void CListBox::updatePositions()
|
||||
(elem)->moveTo(itemPos);
|
||||
itemPos += itemOffset;
|
||||
}
|
||||
if (active)
|
||||
if (isActive())
|
||||
{
|
||||
redraw();
|
||||
if (slider)
|
||||
|
@ -446,7 +446,7 @@ void CGStatusBar::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(!down)
|
||||
{
|
||||
if(LOCPLINT && LOCPLINT->cingconsole->active)
|
||||
if(LOCPLINT && LOCPLINT->cingconsole->isActive())
|
||||
LOCPLINT->cingconsole->startEnteringText();
|
||||
}
|
||||
}
|
||||
@ -574,7 +574,6 @@ void CTextInput::keyPressed(EShortcut key)
|
||||
if(key == EShortcut::GLOBAL_MOVE_FOCUS)
|
||||
{
|
||||
moveFocus();
|
||||
GH.breakEventHandling();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -622,6 +621,9 @@ bool CTextInput::captureThisKey(EShortcut key)
|
||||
if(key == EShortcut::GLOBAL_RETURN)
|
||||
return false;
|
||||
|
||||
if (!focus)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -749,7 +751,7 @@ void CFocusable::moveFocus()
|
||||
if(i == focusables.end())
|
||||
i = focusables.begin();
|
||||
|
||||
if((*i)->active)
|
||||
if((*i)->isActive())
|
||||
{
|
||||
(*i)->giveFocus();
|
||||
break;
|
||||
|
@ -115,13 +115,11 @@ void CBuildingRect::hover(bool on)
|
||||
{
|
||||
if(on)
|
||||
{
|
||||
if(!(active & MOVE))
|
||||
addUsedEvents(MOVE);
|
||||
addUsedEvents(MOVE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(active & MOVE)
|
||||
removeUsedEvents(MOVE);
|
||||
removeUsedEvents(MOVE);
|
||||
|
||||
if(parent->selectedBuilding == this)
|
||||
{
|
||||
@ -218,7 +216,7 @@ void CBuildingRect::showAll(SDL_Surface * to)
|
||||
return;
|
||||
|
||||
CShowableAnim::showAll(to);
|
||||
if(!active && parent->selectedBuilding == this && border)
|
||||
if(!isActive() && parent->selectedBuilding == this && border)
|
||||
border->draw(to, pos.x, pos.y);
|
||||
}
|
||||
|
||||
@ -1577,7 +1575,6 @@ void LabeledValue::hover(bool on)
|
||||
else
|
||||
{
|
||||
GH.statusbar()->clear();
|
||||
parent->hovered = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -972,6 +972,6 @@ std::shared_ptr<CIntObject> CHeroItem::onTabSelected(size_t index)
|
||||
void CHeroItem::onArtChange(int tabIndex)
|
||||
{
|
||||
//redraw item after background change
|
||||
if(active)
|
||||
if(isActive())
|
||||
redraw();
|
||||
}
|
||||
|
@ -295,7 +295,6 @@ void CSpellWindow::fLcornerb()
|
||||
setCurrentPage(currentPage - 1);
|
||||
}
|
||||
computeSpellsPerArea();
|
||||
GH.breakEventHandling();
|
||||
}
|
||||
|
||||
void CSpellWindow::fRcornerb()
|
||||
@ -306,7 +305,6 @@ void CSpellWindow::fRcornerb()
|
||||
setCurrentPage(currentPage + 1);
|
||||
}
|
||||
computeSpellsPerArea();
|
||||
GH.breakEventHandling();
|
||||
}
|
||||
|
||||
void CSpellWindow::show(SDL_Surface * to)
|
||||
|
@ -1479,7 +1479,7 @@ void CAltarWindow::showAll(SDL_Surface * to)
|
||||
int dmp, val;
|
||||
market->getOffer(pickedArt->getTypeId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP);
|
||||
val = static_cast<int>(hero->calculateXp(val));
|
||||
printAtMiddleLoc(std::to_string(val), 304, 498, FONT_SMALL, Colors::WHITE, to);
|
||||
printAtMiddleLoc(std::to_string(val), Point(304, 498), FONT_SMALL, Colors::WHITE, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -534,7 +534,7 @@ void CTavernWindow::show(SDL_Surface * to)
|
||||
recruit->addHoverText(CButton::NORMAL, boost::str(boost::format(CGI->generaltexth->tavernInfo[3]) % sel->h->getNameTranslated() % sel->h->type->heroClass->getNameTranslated()));
|
||||
}
|
||||
|
||||
printAtMiddleWBLoc(sel->description, 146, 395, FONT_SMALL, 200, Colors::WHITE, to);
|
||||
printAtMiddleWBLoc(sel->description, Point(146, 395), FONT_SMALL, 200, Colors::WHITE, to);
|
||||
CSDL_Ext::drawBorder(to,sel->pos.x-2,sel->pos.y-2,sel->pos.w+4,sel->pos.h+4,Colors::BRIGHT_YELLOW);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user