From aacef7212592aecc7c0a94b162ecb963f87d8096 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 3 Jul 2023 17:00:03 +0300 Subject: [PATCH 01/15] Attempt to remove native pinch handling from ios --- client/ios/startSDL.mm | 84 ------------------------------------------ 1 file changed, 84 deletions(-) diff --git a/client/ios/startSDL.mm b/client/ios/startSDL.mm index ce2dbd0a6..24ad9a888 100644 --- a/client/ios/startSDL.mm +++ b/client/ios/startSDL.mm @@ -28,98 +28,14 @@ @implementation SDLViewObserver -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - [object removeObserver:self forKeyPath:keyPath]; - - UIView * view = [object valueForKeyPath:keyPath]; - - auto longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; - longPress.minimumPressDuration = 0.2; - [view addGestureRecognizer:longPress]; - - auto pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; - [view addGestureRecognizer:pinch]; -} - -#pragma mark - Gestures - -- (void)handleLongPress:(UIGestureRecognizer *)gesture { - // send RMB click - SDL_EventType mouseButtonType; - switch (gesture.state) - { - case UIGestureRecognizerStateBegan: - mouseButtonType = SDL_MOUSEBUTTONDOWN; - break; - case UIGestureRecognizerStateEnded: - mouseButtonType = SDL_MOUSEBUTTONUP; - break; - default: - return; - } - - auto renderer = SDL_GetRenderer(mainWindow); - float scaleX, scaleY; - SDL_Rect viewport; - SDL_RenderGetScale(renderer, &scaleX, &scaleY); - SDL_RenderGetViewport(renderer, &viewport); - - auto touchedPoint = [gesture locationInView:gesture.view]; - auto screenScale = UIScreen.mainScreen.nativeScale; - Sint32 x = (int)touchedPoint.x * screenScale / scaleX - viewport.x; - Sint32 y = (int)touchedPoint.y * screenScale / scaleY - viewport.y; - - SDL_Event rmbEvent; - rmbEvent.button = (SDL_MouseButtonEvent){ - .type = mouseButtonType, - .button = SDL_BUTTON_RIGHT, - .clicks = 1, - .x = x, - .y = y, - }; - SDL_PushEvent(&rmbEvent); - - // small hack to prevent cursor jumping - if (mouseButtonType == SDL_MOUSEBUTTONUP) - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.025 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - SDL_Event motionEvent; - motionEvent.motion = (SDL_MouseMotionEvent){ - .type = SDL_MOUSEMOTION, - .x = x, - .y = y, - }; - SDL_PushEvent(&motionEvent); - }); -} - -- (void)handlePinch:(UIGestureRecognizer *)gesture { - if(gesture.state != UIGestureRecognizerStateBegan || CSH->state != EClientState::GAMEPLAY) - return; - [GameChatKeyboardHandler sendKeyEventWithKeyCode:SDLK_SPACE]; -} - -#pragma mark - UIGestureRecognizerDelegate - -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { - return [gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]; -} - @end - int startSDL(int argc, char * argv[], BOOL startManually) { @autoreleasepool { auto observer = [SDLViewObserver new]; observer.gameChatHandler = [GameChatKeyboardHandler new]; - id __block sdlWindowCreationObserver = [NSNotificationCenter.defaultCenter addObserverForName:UIWindowDidBecomeKeyNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { - [NSNotificationCenter.defaultCenter removeObserver:sdlWindowCreationObserver]; - sdlWindowCreationObserver = nil; - - UIWindow * sdlWindow = note.object; - [sdlWindow.rootViewController addObserver:observer forKeyPath:NSStringFromSelector(@selector(view)) options:NSKeyValueObservingOptionNew context:NULL]; - }]; id textFieldObserver = [NSNotificationCenter.defaultCenter addObserverForName:UITextFieldTextDidEndEditingNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { removeFocusFromActiveInput(); }]; From 48eb8ab54f9992eff22a27af54e9d87b9e02cd97 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 3 Jul 2023 17:12:31 +0300 Subject: [PATCH 02/15] Play sound when receiving message, not just on sending ours --- client/adventureMap/CInGameConsole.cpp | 6 +----- server/CGameHandler.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index 735a92ac4..ea8f52d33 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -104,6 +104,7 @@ void CInGameConsole::print(const std::string & txt) } GH.windows().totalRedraw(); // FIXME: ingame console has no parent widget set + CCS->soundh->playSound("CHAT"); } void CInGameConsole::keyPressed (EShortcut key) @@ -134,11 +135,6 @@ void CInGameConsole::keyPressed (EShortcut key) { bool anyTextExceptCaret = enteredText.size() > 1; endEnteringText(anyTextExceptCaret); - - if(anyTextExceptCaret) - { - CCS->soundh->playSound("CHAT"); - } } break; } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b26b0abd2..24aa8357f 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4980,12 +4980,6 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message, { bool cheated = false; - if(!getPlayerSettings(player)->isControlledByAI()) - { - PlayerMessageClient temp_message(player, message); - sendAndApply(&temp_message); - } - std::vector words; boost::split(words, message, boost::is_any_of(" ")); @@ -5106,6 +5100,14 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message, if(!player.isSpectator()) checkVictoryLossConditionsForPlayer(player);//Player enter win code or got required art\creature } + else + { + if(!getPlayerSettings(player)->isControlledByAI()) + { + PlayerMessageClient temp_message(player, message); + sendAndApply(&temp_message); + } + } } bool CGameHandler::makeCustomAction(BattleAction & ba) From 8b61c0d59b1da149e9442c6cfe904e5af39497ac Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 3 Jul 2023 19:24:12 +0300 Subject: [PATCH 03/15] Replaced CIntObject::type bitfield with bool's. Added input blocking. --- client/adventureMap/AdventureMapInterface.cpp | 14 +---- client/adventureMap/CInGameConsole.cpp | 2 +- client/gui/CIntObject.cpp | 52 ++++++++++++++----- client/gui/CIntObject.h | 14 +++-- client/gui/InterfaceObjectConfigurable.cpp | 2 +- client/lobby/CSelectionBase.cpp | 4 +- client/lobby/RandomMapTab.cpp | 2 +- client/lobby/SelectionTab.cpp | 2 +- client/mainmenu/CMainMenu.cpp | 6 +-- client/mainmenu/CreditsScreen.cpp | 2 +- client/widgets/CComponent.cpp | 8 +-- client/widgets/CreatureCostBox.cpp | 2 +- client/widgets/MiscWidgets.h | 1 + client/widgets/TextControls.cpp | 6 +-- client/windows/CCastleInterface.cpp | 2 +- client/windows/GUIClasses.cpp | 4 +- .../windows/settings/AdventureOptionsTab.cpp | 2 +- client/windows/settings/BattleOptionsTab.cpp | 2 +- client/windows/settings/GeneralOptionsTab.cpp | 2 +- .../windows/settings/SettingsMainWindow.cpp | 2 +- 20 files changed, 77 insertions(+), 54 deletions(-) diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 943c50d1e..a4440d76f 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -318,7 +318,6 @@ void AdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID) widget->getMinimap()->setAIRadar(true); widget->getInfoBar()->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer()); setState(EAdventureState::ENEMY_TURN); - } void AdventureMapInterface::setState(EAdventureState state) @@ -333,17 +332,8 @@ void AdventureMapInterface::adjustActiveness() bool widgetMustBeActive = isActive() && shortcuts->optionSidePanelActive(); bool mapViewMustBeActive = isActive() && (shortcuts->optionMapViewActive()); - if (widgetMustBeActive && !widget->isActive()) - widget->activate(); - - if (!widgetMustBeActive && widget->isActive()) - widget->deactivate(); - - if (mapViewMustBeActive && !widget->getMapView()->isActive()) - widget->getMapView()->activate(); - - if (!mapViewMustBeActive && widget->getMapView()->isActive()) - widget->getMapView()->deactivate(); + widget->setInputEnabled(widgetMustBeActive); + widget->getMapView()->setInputEnabled(mapViewMustBeActive); } void AdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID) diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index ea8f52d33..19d4af8d5 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -34,7 +34,7 @@ CInGameConsole::CInGameConsole() : CIntObject(KEYBOARD | TIME | TEXTINPUT) , prevEntDisp(-1) { - type |= REDRAW_PARENT; + setRedrawParent(true); } void CInGameConsole::showAll(Canvas & to) diff --git a/client/gui/CIntObject.cpp b/client/gui/CIntObject.cpp index 052c8e4c8..59dcd15ae 100644 --- a/client/gui/CIntObject.cpp +++ b/client/gui/CIntObject.cpp @@ -20,18 +20,14 @@ CIntObject::CIntObject(int used_, Point pos_): parent_m(nullptr), parent(parent_m), - type(0) + redrawParent(false), + inputEnabled(true), + captureAllKeys(false), + used(used_), + recActions(GH.defActionsDef), + defActions(GH.defActionsDef), + pos(pos_, Point()) { - captureAllKeys = false; - used = used_; - - recActions = defActions = GH.defActionsDef; - - pos.x = pos_.x; - pos.y = pos_.y; - pos.w = 0; - pos.h = 0; - if(GH.captureChildren) GH.createdObj.front()->addChild(this, true); } @@ -76,7 +72,11 @@ void CIntObject::activate() if (isActive()) return; - activateEvents(used | GENERAL); + if (inputEnabled) + activateEvents(used | GENERAL); + else + activateEvents(GENERAL); + assert(isActive()); if(defActions & ACTIVATE) @@ -141,6 +141,32 @@ void CIntObject::setEnabled(bool on) disable(); } +void CIntObject::setInputEnabled(bool on) +{ + if (inputEnabled == on) + return; + + inputEnabled = on; + + if (!isActive()) + return; + + assert((used & GENERAL) == 0); + + if (on) + activateEvents(used); + else + deactivateEvents(used); + + for(auto & elem : children) + elem->setInputEnabled(on); +} + +void CIntObject::setRedrawParent(bool on) +{ + redrawParent = on; +} + void CIntObject::fitToScreen(int borderWidth, bool propagate) { Point newPos = pos.topLeft(); @@ -210,7 +236,7 @@ void CIntObject::redraw() //it should fix glitches when called by inactive elements located below active window if (isActive()) { - if (parent_m && (type & REDRAW_PARENT)) + if (parent_m && redrawParent) { parent_m->redraw(); } diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index 265097837..aa3ab280c 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -47,11 +47,10 @@ class CIntObject : public IShowActivatable, public AEventsReceiver //interface o //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 + bool inputEnabled; + bool redrawParent; +public: std::vector children; /// read-only parent access. May not be a "clean" solution but allows some compatibility @@ -82,6 +81,13 @@ public: /// deactivates or activates UI element based on flag void setEnabled(bool on); + /// Block (or allow) all user input, e.g. mouse/keyboard/touch without hiding element + void setInputEnabled(bool on); + + /// Mark this input as one that requires parent redraw on update, + /// for example if current control might have semi-transparent elements and requires redrawing of background + void setRedrawParent(bool on); + // activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse) // usually used automatically by parent void activate() override; diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index bcc430adc..aba2998bc 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -493,7 +493,7 @@ public: InterfaceLayoutWidget::InterfaceLayoutWidget() :CIntObject() { - type |= REDRAW_PARENT; + setRedrawParent(true); } std::shared_ptr InterfaceObjectConfigurable::buildLayout(const JsonNode & config) diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index dfe2461ab..4971011bc 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -115,7 +115,7 @@ InfoCard::InfoCard() : showChat(true) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - CIntObject::type |= REDRAW_PARENT; + setRedrawParent(true); pos.x += 393; pos.y += 6; @@ -311,7 +311,7 @@ CChatBox::CChatBox(const Rect & rect) OBJ_CONSTRUCTION; pos += rect.topLeft(); captureAllKeys = true; - type |= REDRAW_PARENT; + setRedrawParent(true); const int height = static_cast(graphics->fonts[FONT_SMALL]->getLineHeight()); inputBox = std::make_shared(Rect(0, rect.h - height, rect.w, height), EFonts::FONT_SMALL, 0); diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index efbf1825c..9533bf174 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -371,7 +371,7 @@ TemplatesDropBox::ListItem::ListItem(const JsonNode & config, TemplatesDropBox & pos.w = w->pos.w; pos.h = w->pos.h; } - type |= REDRAW_PARENT; + setRedrawParent(true); } void TemplatesDropBox::ListItem::updateItem(int idx, const CRmgTemplate * _item) diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 9242bc7db..9cecc612e 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -179,7 +179,7 @@ SelectionTab::SelectionTab(ESelectionScreen Type) break; case ESelectionScreen::campaignList: tabTitle = CGI->generaltexth->allTexts[726]; - type |= REDRAW_PARENT; // we use parent background so we need to make sure it's will be redrawn too + setRedrawParent(true); // we use parent background so we need to make sure it's will be redrawn too pos.w = parent->pos.w; pos.h = parent->pos.h; pos.x += 3; diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index 20f0789f9..5cc349a59 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -95,7 +95,7 @@ CMenuScreen::CMenuScreen(const JsonNode & configNode) menuNameToEntry.push_back("credits"); tabs = std::make_shared(std::bind(&CMenuScreen::createTab, this, _1)); - tabs->type |= REDRAW_PARENT; + tabs->setRedrawParent(true); } std::shared_ptr CMenuScreen::createTab(size_t index) @@ -248,7 +248,7 @@ std::shared_ptr CMenuEntry::createButton(CMenuScreen * parent, const Js CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - type |= REDRAW_PARENT; + setRedrawParent(true); pos = parent->pos; for(const JsonNode & node : config["images"].Vector()) @@ -258,7 +258,7 @@ CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config) { buttons.push_back(createButton(parent, node)); buttons.back()->hoverable = true; - buttons.back()->type |= REDRAW_PARENT; + buttons.back()->setRedrawParent(true); } } diff --git a/client/mainmenu/CreditsScreen.cpp b/client/mainmenu/CreditsScreen.cpp index a7946fd97..7c1c140ce 100644 --- a/client/mainmenu/CreditsScreen.cpp +++ b/client/mainmenu/CreditsScreen.cpp @@ -24,7 +24,7 @@ CreditsScreen::CreditsScreen(Rect rect) { pos.w = rect.w; pos.h = rect.h; - type |= REDRAW_PARENT; + setRedrawParent(true); OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; auto textFile = CResourceHandler::get()->load(ResourceID("DATA/CREDITS.TXT"))->readAll(); std::string text((char *)textFile.first.get(), textFile.second); diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index da1498c31..7d615e975 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -281,7 +281,7 @@ void CSelectableComponent::init() CSelectableComponent::CSelectableComponent(const Component &c, std::function OnSelect): CComponent(c),onSelect(OnSelect) { - type |= REDRAW_PARENT; + setRedrawParent(true); addUsedEvents(LCLICK | KEYBOARD); init(); } @@ -289,7 +289,7 @@ CSelectableComponent::CSelectableComponent(const Component &c, std::function OnSelect): CComponent(Type,Sub,Val, imageSize),onSelect(OnSelect) { - type |= REDRAW_PARENT; + setRedrawParent(true); addUsedEvents(LCLICK | KEYBOARD); init(); } @@ -466,7 +466,7 @@ CComponentBox::CComponentBox(std::vector> _component betweenRows(betweenRows), componentsInRow(componentsInRow) { - type |= REDRAW_PARENT; + setRedrawParent(true); pos = position + pos.topLeft(); placeComponents(false); } @@ -484,7 +484,7 @@ CComponentBox::CComponentBox(std::vector> betweenRows(betweenRows), componentsInRow(componentsInRow) { - type |= REDRAW_PARENT; + setRedrawParent(true); pos = position + pos.topLeft(); placeComponents(true); diff --git a/client/widgets/CreatureCostBox.cpp b/client/widgets/CreatureCostBox.cpp index d41242d8d..94c31e29d 100644 --- a/client/widgets/CreatureCostBox.cpp +++ b/client/widgets/CreatureCostBox.cpp @@ -17,7 +17,7 @@ CreatureCostBox::CreatureCostBox(Rect position, std::string titleText) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - type |= REDRAW_PARENT; + setRedrawParent(true); pos = position + pos.topLeft(); title = std::make_shared(pos.w/2, 10, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, titleText); diff --git a/client/widgets/MiscWidgets.h b/client/widgets/MiscWidgets.h index 2f572c9f1..051f840d3 100644 --- a/client/widgets/MiscWidgets.h +++ b/client/widgets/MiscWidgets.h @@ -143,6 +143,7 @@ public: class LRClickableAreaWTextComp: public LRClickableAreaWText { public: + int type; int baseType; int bonusValue; virtual void clickLeft(tribool down, bool previousState) override; diff --git a/client/widgets/TextControls.cpp b/client/widgets/TextControls.cpp index f3089be80..88e384a5f 100644 --- a/client/widgets/TextControls.cpp +++ b/client/widgets/TextControls.cpp @@ -47,7 +47,7 @@ void CLabel::showAll(Canvas & to) CLabel::CLabel(int x, int y, EFonts Font, ETextAlignment Align, const SDL_Color & Color, const std::string & Text) : CTextContainer(Align, Font, Color), text(Text) { - type |= REDRAW_PARENT; + setRedrawParent(true); autoRedraw = true; pos.x += x; pos.y += y; @@ -299,7 +299,7 @@ CTextBox::CTextBox(std::string Text, const Rect & rect, int SliderStyle, EFonts OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); label = std::make_shared(rect, Font, Align, Color); - type |= REDRAW_PARENT; + setRedrawParent(true); pos.x += rect.x; pos.y += rect.y; pos.h = rect.h; @@ -492,7 +492,7 @@ CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList(this)) { - type |= REDRAW_PARENT; + setRedrawParent(true); pos.h = Pos.h; pos.w = Pos.w; captureAllKeys = true; diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 91c6333d7..e8598f635 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -1160,7 +1160,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst updateShadow(); garr = std::make_shared(305, 387, 4, Point(0,96), town->getUpperArmy(), town->visitingHero); - garr->type |= REDRAW_PARENT; + garr->setRedrawParent(true); heroes = std::make_shared(town, Point(241, 387), Point(241, 483), garr, true); title = std::make_shared(85, 387, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, town->getNameTranslated()); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 172596d12..4f4a3e4bc 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1776,7 +1776,7 @@ CObjectListWindow::CItem::CItem(CObjectListWindow * _parent, size_t _id, std::st border = std::make_shared("TPGATES"); pos = border->pos; - type |= REDRAW_PARENT; + setRedrawParent(true); text = std::make_shared(pos.w/2, pos.h/2, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, _text); select(index == parent->selected); @@ -1850,7 +1850,7 @@ void CObjectListWindow::init(std::shared_ptr titleWidget_, std::stri } list = std::make_shared(std::bind(&CObjectListWindow::genItem, this, _1), Point(14, 151), Point(0, 25), 9, items.size(), 0, 1, Rect(262, -32, 256, 256) ); - list->type |= REDRAW_PARENT; + list->setRedrawParent(true); ok = std::make_shared(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), EShortcut::GLOBAL_ACCEPT); ok->block(!list->size()); diff --git a/client/windows/settings/AdventureOptionsTab.cpp b/client/windows/settings/AdventureOptionsTab.cpp index 06c563ce7..c22d16e7a 100644 --- a/client/windows/settings/AdventureOptionsTab.cpp +++ b/client/windows/settings/AdventureOptionsTab.cpp @@ -34,7 +34,7 @@ AdventureOptionsTab::AdventureOptionsTab() : InterfaceObjectConfigurable() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - type |= REDRAW_PARENT; + setRedrawParent(true); const JsonNode config(ResourceID("config/widgets/settings/adventureOptionsTab.json")); addCallback("playerHeroSpeedChanged", [this](int value) diff --git a/client/windows/settings/BattleOptionsTab.cpp b/client/windows/settings/BattleOptionsTab.cpp index 76b6b2815..475fb604e 100644 --- a/client/windows/settings/BattleOptionsTab.cpp +++ b/client/windows/settings/BattleOptionsTab.cpp @@ -21,7 +21,7 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - type |= REDRAW_PARENT; + setRedrawParent(true); const JsonNode config(ResourceID("config/widgets/settings/battleOptionsTab.json")); addCallback("viewGridChanged", [this, owner](bool value) diff --git a/client/windows/settings/GeneralOptionsTab.cpp b/client/windows/settings/GeneralOptionsTab.cpp index 5b23d2c55..4710067cc 100644 --- a/client/windows/settings/GeneralOptionsTab.cpp +++ b/client/windows/settings/GeneralOptionsTab.cpp @@ -94,7 +94,7 @@ GeneralOptionsTab::GeneralOptionsTab() onFullscreenChanged(settings.listen["video"]["fullscreen"]) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - type |= REDRAW_PARENT; + setRedrawParent(true); addConditional("touchscreen", GH.input().hasTouchInputDevice()); #ifdef VCMI_MOBILE diff --git a/client/windows/settings/SettingsMainWindow.cpp b/client/windows/settings/SettingsMainWindow.cpp index 64b9c8a7c..c05592e7d 100644 --- a/client/windows/settings/SettingsMainWindow.cpp +++ b/client/windows/settings/SettingsMainWindow.cpp @@ -71,7 +71,7 @@ SettingsMainWindow::SettingsMainWindow(BattleInterface * parentBattleUi) : Inter parentBattleInterface = parentBattleUi; tabContentArea = std::make_shared(std::bind(&SettingsMainWindow::createTab, this, _1), Point(0, 0), defaultTabIndex); - tabContentArea->type |= REDRAW_PARENT; + tabContentArea->setRedrawParent(true); std::shared_ptr mainTabs = widget("settingsTabs"); mainTabs->setSelected(defaultTabIndex); From 4e80356beadddff0e7b16a961eb92ec745e9fa8e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 3 Jul 2023 19:36:10 +0300 Subject: [PATCH 04/15] Do not hide minimap on human player turn --- client/CPlayerInterface.cpp | 5 ++++- client/adventureMap/AdventureMapInterface.cpp | 4 ++-- client/adventureMap/AdventureMapInterface.h | 2 +- client/adventureMap/CMinimap.cpp | 2 +- client/adventureMap/CMinimap.h | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 595ade466..b40d078b3 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -183,7 +183,10 @@ void CPlayerInterface::playerStartsTurn(PlayerColor player) if (player != playerID && LOCPLINT == this) { waitWhileDialog(); - adventureInt->onEnemyTurnStarted(player); + + bool isHuman = cb->getStartInfo()->playerInfos.count(player) && cb->getStartInfo()->playerInfos.at(player).isControlledByHuman(); + + adventureInt->onEnemyTurnStarted(player, isHuman); } } diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index a4440d76f..fc10a67d0 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -309,13 +309,13 @@ void AdventureMapInterface::onHotseatWaitStarted(PlayerColor playerID) setState(EAdventureState::HOTSEAT_WAIT); } -void AdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID) +void AdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID, bool isHuman) { if(settings["session"]["spectate"].Bool()) return; mapAudio->onEnemyTurnStarted(); - widget->getMinimap()->setAIRadar(true); + widget->getMinimap()->setAIRadar(!isHuman); widget->getInfoBar()->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer()); setState(EAdventureState::ENEMY_TURN); } diff --git a/client/adventureMap/AdventureMapInterface.h b/client/adventureMap/AdventureMapInterface.h index 488778bfc..6f69b2cdd 100644 --- a/client/adventureMap/AdventureMapInterface.h +++ b/client/adventureMap/AdventureMapInterface.h @@ -115,7 +115,7 @@ public: void onHotseatWaitStarted(PlayerColor playerID); /// Called by PlayerInterface when AI or remote human player starts his turn - void onEnemyTurnStarted(PlayerColor playerID); + void onEnemyTurnStarted(PlayerColor playerID, bool isHuman); /// Called by PlayerInterface when local human player starts his turn void onPlayerTurnStarted(PlayerColor playerID); diff --git a/client/adventureMap/CMinimap.cpp b/client/adventureMap/CMinimap.cpp index 57c740fcd..5fa99358f 100644 --- a/client/adventureMap/CMinimap.cpp +++ b/client/adventureMap/CMinimap.cpp @@ -231,7 +231,7 @@ void CMinimap::setAIRadar(bool on) redraw(); } -void CMinimap::updateTiles(std::unordered_set positions) +void CMinimap::updateTiles(const std::unordered_set & positions) { if(minimap) { diff --git a/client/adventureMap/CMinimap.h b/client/adventureMap/CMinimap.h index 496611bb5..76eb4c95d 100644 --- a/client/adventureMap/CMinimap.h +++ b/client/adventureMap/CMinimap.h @@ -68,6 +68,6 @@ public: void showAll(Canvas & to) override; - void updateTiles(std::unordered_set positions); + void updateTiles(const std::unordered_set & positions); }; From 81b9aec5273fd9e5c82dd77b63dceaaa32a40bc9 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 3 Jul 2023 23:50:09 +0300 Subject: [PATCH 05/15] Fix map edge scrolling after swiping usage --- client/gui/EventDispatcher.cpp | 4 +++- client/mapView/MapView.cpp | 9 +-------- client/mapView/MapView.h | 5 ----- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/client/gui/EventDispatcher.cpp b/client/gui/EventDispatcher.cpp index 93c0785a5..25398ddd4 100644 --- a/client/gui/EventDispatcher.cpp +++ b/client/gui/EventDispatcher.cpp @@ -63,7 +63,9 @@ void EventDispatcher::dispatchTimer(uint32_t msPassed) EventReceiversList hlp = timeinterested; for (auto & elem : hlp) { - if(!vstd::contains(timeinterested,elem)) continue; + if(!vstd::contains(timeinterested,elem)) + continue; + elem->tick(msPassed); } } diff --git a/client/mapView/MapView.cpp b/client/mapView/MapView.cpp index a83c0731c..76f981337 100644 --- a/client/mapView/MapView.cpp +++ b/client/mapView/MapView.cpp @@ -90,7 +90,6 @@ void MapView::show(Canvas & to) MapView::MapView(const Point & offset, const Point & dimensions) : BasicMapView(offset, dimensions) - , isSwiping(false) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; actions = std::make_shared(*this, model); @@ -110,21 +109,15 @@ void MapView::onMapLevelSwitched() void MapView::onMapScrolled(const Point & distance) { - if(!isSwiping) + if(!isGesturing()) controller->setViewCenter(model->getMapViewCenter() + distance, model->getLevel()); } void MapView::onMapSwiped(const Point & viewPosition) { - isSwiping = true; controller->setViewCenter(model->getMapViewCenter() + viewPosition, model->getLevel()); } -void MapView::onMapSwipeEnded() -{ - isSwiping = false; -} - void MapView::onCenteredTile(const int3 & tile) { controller->setViewCenter(tile); diff --git a/client/mapView/MapView.h b/client/mapView/MapView.h index c5dea4ce4..a3434dc1a 100644 --- a/client/mapView/MapView.h +++ b/client/mapView/MapView.h @@ -48,8 +48,6 @@ class MapView : public BasicMapView { std::shared_ptr actions; - bool isSwiping; - public: void show(Canvas & to) override; @@ -64,9 +62,6 @@ public: /// Moves current view to specified position, in pixels void onMapSwiped(const Point & viewPosition); - /// Ends swiping mode and allows normal map scrolling once again - void onMapSwipeEnded(); - /// Moves current view to specified tile void onCenteredTile(const int3 & tile); From 900b1c17630e66714e87e8921094ad0a06683b64 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 18:42:52 +0300 Subject: [PATCH 06/15] Simplified text input handling, fixes hotkeys on windows with text input --- client/adventureMap/CInGameConsole.cpp | 38 +++++++++++++++++------- client/adventureMap/CInGameConsole.h | 1 + client/eventsSDL/InputSourceKeyboard.cpp | 12 ++++++++ client/gui/CIntObject.cpp | 25 +++++++++------- client/gui/CIntObject.h | 6 ++-- client/lobby/CSelectionBase.cpp | 1 - client/widgets/TextControls.cpp | 14 --------- client/widgets/TextControls.h | 2 +- 8 files changed, 58 insertions(+), 41 deletions(-) diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index 19d4af8d5..3236a5bc2 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -96,7 +96,7 @@ void CInGameConsole::print(const std::string & txt) auto splitText = CMessage::breakText(txt, maxWidth, FONT_MEDIUM); - for (auto const & entry : splitText) + for(const auto & entry : splitText) texts.push_back({entry, 0}); while(texts.size() > maxDisplayedTexts) @@ -107,23 +107,43 @@ void CInGameConsole::print(const std::string & txt) CCS->soundh->playSound("CHAT"); } +bool CInGameConsole::captureThisKey(EShortcut key) +{ + if (enteredText.empty()) + return false; + + switch (key) + { + case EShortcut::GLOBAL_ACCEPT: + case EShortcut::GLOBAL_CANCEL: + case EShortcut::GAME_ACTIVATE_CONSOLE: + case EShortcut::GLOBAL_BACKSPACE: + case EShortcut::MOVE_UP: + case EShortcut::MOVE_DOWN: + return true; + + default: + return false; + } +} + void CInGameConsole::keyPressed (EShortcut key) { if (LOCPLINT->cingconsole != this) return; - if(!captureAllKeys && key != EShortcut::GAME_ACTIVATE_CONSOLE) + if(enteredText.empty() && key != EShortcut::GAME_ACTIVATE_CONSOLE) return; //because user is not entering any text switch(key) { case EShortcut::GLOBAL_CANCEL: - if(captureAllKeys) + if(!enteredText.empty()) endEnteringText(false); break; case EShortcut::GAME_ACTIVATE_CONSOLE: - if(captureAllKeys) + if(!enteredText.empty()) endEnteringText(false); else startEnteringText(); @@ -131,7 +151,7 @@ void CInGameConsole::keyPressed (EShortcut key) case EShortcut::GLOBAL_ACCEPT: { - if(!enteredText.empty() && captureAllKeys) + if(!enteredText.empty()) { bool anyTextExceptCaret = enteredText.size() > 1; endEnteringText(anyTextExceptCaret); @@ -191,8 +211,9 @@ void CInGameConsole::textInputed(const std::string & inputtedText) if (LOCPLINT->cingconsole != this) return; - if(!captureAllKeys || enteredText.empty()) + if(enteredText.empty()) return; + enteredText.resize(enteredText.size()-1); enteredText += inputtedText; @@ -211,14 +232,10 @@ void CInGameConsole::startEnteringText() if (!isActive()) return; - if (captureAllKeys) - return; - assert(currentStatusBar.expired());//effectively, nullptr check currentStatusBar = GH.statusbar(); - captureAllKeys = true; enteredText = "_"; GH.statusbar()->setEnteringMode(true); @@ -227,7 +244,6 @@ void CInGameConsole::startEnteringText() void CInGameConsole::endEnteringText(bool processEnteredText) { - captureAllKeys = false; prevEntDisp = -1; if(processEnteredText) { diff --git a/client/adventureMap/CInGameConsole.h b/client/adventureMap/CInGameConsole.h index 64b87afbe..8b7ffab86 100644 --- a/client/adventureMap/CInGameConsole.h +++ b/client/adventureMap/CInGameConsole.h @@ -50,6 +50,7 @@ public: void keyPressed(EShortcut key) override; void textInputed(const std::string & enteredText) override; void textEdited(const std::string & enteredText) override; + bool captureThisKey(EShortcut key) override; void startEnteringText(); void endEnteringText(bool processEnteredText); diff --git a/client/eventsSDL/InputSourceKeyboard.cpp b/client/eventsSDL/InputSourceKeyboard.cpp index adce3271b..7c953063c 100644 --- a/client/eventsSDL/InputSourceKeyboard.cpp +++ b/client/eventsSDL/InputSourceKeyboard.cpp @@ -33,6 +33,12 @@ void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key) if(key.repeat != 0) return; // ignore periodic event resends + if (SDL_IsTextInputActive() == SDL_TRUE) + { + if (key.keysym.sym >= ' ' && key.keysym.sym < 0x80) + return; // printable character - will be handled as text input + } + assert(key.state == SDL_PRESSED); if(key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool()) @@ -77,6 +83,12 @@ void InputSourceKeyboard::handleEventKeyUp(const SDL_KeyboardEvent & key) if(key.repeat != 0) return; // ignore periodic event resends + if (SDL_IsTextInputActive() == SDL_TRUE) + { + if (key.keysym.sym >= ' ' && key.keysym.sym < 0x80) + return; // printable character - will be handled as text input + } + assert(key.state == SDL_RELEASED); auto shortcutsVector = GH.shortcuts().translateKeycode(key.keysym.sym); diff --git a/client/gui/CIntObject.cpp b/client/gui/CIntObject.cpp index 59dcd15ae..d82dc6dde 100644 --- a/client/gui/CIntObject.cpp +++ b/client/gui/CIntObject.cpp @@ -12,6 +12,7 @@ #include "CGuiHandler.h" #include "WindowHandler.h" +#include "EventDispatcher.h" #include "Shortcut.h" #include "../render/Canvas.h" #include "../windows/CMessage.h" @@ -22,7 +23,6 @@ CIntObject::CIntObject(int used_, Point pos_): parent(parent_m), redrawParent(false), inputEnabled(true), - captureAllKeys(false), used(used_), recActions(GH.defActionsDef), defActions(GH.defActionsDef), @@ -37,6 +37,8 @@ CIntObject::~CIntObject() if(isActive()) deactivate(); + GH.events().assertElementInactive(this); + while(!children.empty()) { if((defActions & DISPOSE) && (children.front()->recActions & DISPOSE)) @@ -148,15 +150,15 @@ void CIntObject::setInputEnabled(bool on) inputEnabled = on; - if (!isActive()) - return; + if (isActive()) + { + assert((used & GENERAL) == 0); - assert((used & GENERAL) == 0); - - if (on) - activateEvents(used); - else - deactivateEvents(used); + if (on) + activateEvents(used); + else + deactivateEvents(used); + } for(auto & elem : children) elem->setInputEnabled(on); @@ -207,6 +209,9 @@ void CIntObject::addChild(CIntObject * child, bool adjustPosition) if(adjustPosition) child->moveBy(pos.topLeft(), adjustPosition); + if (inputEnabled != child->inputEnabled) + child->setInputEnabled(inputEnabled); + if (!isActive() && child->isActive()) child->deactivate(); if (isActive()&& !child->isActive()) @@ -292,7 +297,7 @@ const Rect & CIntObject::center(const Point & p, bool propagate) bool CIntObject::captureThisKey(EShortcut key) { - return captureAllKeys; + return false; } CKeyShortcut::CKeyShortcut() diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index aa3ab280c..da88cf9f3 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -62,10 +62,8 @@ public: CIntObject(int used=0, Point offset=Point()); virtual ~CIntObject(); - //keyboard handling - bool captureAllKeys; //if true, only this object should get info about pressed keys - - bool captureThisKey(EShortcut key) override; //allows refining captureAllKeys against specific events (eg. don't capture ENTER) + /// allows capturing key input so it will be delivered only to this element + bool captureThisKey(EShortcut key) override; void addUsedEvents(ui16 newActions); void removeUsedEvents(ui16 newActions); diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index 4971011bc..c6e04372a 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -310,7 +310,6 @@ CChatBox::CChatBox(const Rect & rect) { OBJ_CONSTRUCTION; pos += rect.topLeft(); - captureAllKeys = true; setRedrawParent(true); const int height = static_cast(graphics->fonts[FONT_SMALL]->getLineHeight()); diff --git a/client/widgets/TextControls.cpp b/client/widgets/TextControls.cpp index 88e384a5f..71182d6a3 100644 --- a/client/widgets/TextControls.cpp +++ b/client/widgets/TextControls.cpp @@ -495,7 +495,6 @@ CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList(bgName, bgOffset.x, bgOffset.y); addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT); @@ -525,7 +523,6 @@ CTextInput::CTextInput(const Rect & Pos, std::shared_ptr srf) :CFocusable(std::make_shared(this)) { pos += Pos.topLeft(); - captureAllKeys = true; OBJ_CONSTRUCTION; background = std::make_shared(srf, Pos); pos.w = background->pos.w; @@ -620,17 +617,6 @@ void CTextInput::setText(const std::string & nText, bool callCb) cb(text); } -bool CTextInput::captureThisKey(EShortcut key) -{ - if(key == EShortcut::GLOBAL_RETURN) - return false; - - if (!focus) - return false; - - return true; -} - void CTextInput::textInputed(const std::string & enteredText) { if(!focus) diff --git a/client/widgets/TextControls.h b/client/widgets/TextControls.h index c5d4370c1..4e5250737 100644 --- a/client/widgets/TextControls.h +++ b/client/widgets/TextControls.h @@ -227,7 +227,7 @@ public: void clickLeft(tribool down, bool previousState) override; void keyPressed(EShortcut key) override; - bool captureThisKey(EShortcut key) override; + //bool captureThisKey(EShortcut key) override; void textInputed(const std::string & enteredText) override; void textEdited(const std::string & enteredText) override; From f914ec9d575432c368347ae5ad2e5338db6ef944 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 19:00:35 +0300 Subject: [PATCH 07/15] Fix activation of in-game console in other windows --- client/adventureMap/AdventureMapInterface.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index fc10a67d0..a67fc13ed 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -134,12 +134,15 @@ void AdventureMapInterface::activate() } void AdventureMapInterface::deactivate() -{ - CIntObject::deactivate(); - CCS->curh->set(Cursor::Map::POINTER); -} - -void AdventureMapInterface::showAll(Canvas & to) +{ + CIntObject::deactivate(); + CCS->curh->set(Cursor::Map::POINTER); + + if(LOCPLINT) + LOCPLINT->cingconsole->deactivate(); +} + +void AdventureMapInterface::showAll(Canvas & to) { CIntObject::showAll(to); LOCPLINT->cingconsole->show(to); From 4fd5a2b3eaea12925428d4b7e2b77ec30a99e3ae Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 19:29:01 +0300 Subject: [PATCH 08/15] Fixed sorting maps breaking after multiple clicks in succession --- client/lobby/SelectionTab.cpp | 15 +++++++++++++-- client/lobby/SelectionTab.h | 5 +++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 9cecc612e..6b85b8f9d 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -457,10 +457,21 @@ void SelectionTab::updateListItems() } } -int SelectionTab::getLine() +bool SelectionTab::receiveEvent(const Point & position, int eventType) const +{ + // FIXME: widget should instead have well-defined pos so events will be filtered using standard routine + return getLine(position) != -1; +} + +int SelectionTab::getLine() const +{ + Point clickPos = GH.getCursorPosition() - pos.topLeft(); + return getLine(clickPos); +} + +int SelectionTab::getLine(const Point & clickPos) const { int line = -1; - Point clickPos = GH.getCursorPosition() - pos.topLeft(); // Ignore clicks on save name area int maxPosY; diff --git a/client/lobby/SelectionTab.h b/client/lobby/SelectionTab.h index 48e79f991..6484c802d 100644 --- a/client/lobby/SelectionTab.h +++ b/client/lobby/SelectionTab.h @@ -67,8 +67,8 @@ public: void clickLeft(tribool down, bool previousState) override; void keyPressed(EShortcut key) override; - void clickDouble() override; + bool receiveEvent(const Point & position, int eventType) const override; void filter(int size, bool selectFirst = false); //0 - all void sortBy(int criteria); @@ -77,7 +77,8 @@ public: void selectAbs(int position); //position: absolute position in curItems vector void sliderMove(int slidPos); void updateListItems(); - int getLine(); + int getLine() const; + int getLine(const Point & position) const; void selectFileName(std::string fname); std::shared_ptr getSelectedMapInfo() const; void rememberCurrentSelection(); From 1cbc6457cea329958b63b6e7b5c9ffbddba46da9 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 20:02:41 +0300 Subject: [PATCH 09/15] Fix possible assertion failure on removing non-owned town --- AI/Nullkiller/AIGateway.cpp | 6 ++++++ AI/Nullkiller/AIGateway.h | 1 + AI/VCAI/VCAI.cpp | 5 +++++ AI/VCAI/VCAI.h | 1 + client/CPlayerInterface.cpp | 32 +++++++++++++++++++++++++------- client/CPlayerInterface.h | 1 + client/ClientNetPackVisitors.h | 3 ++- client/NetPacksClient.cpp | 19 +++++++++++++++---- lib/IGameEventsReceiver.h | 1 + 9 files changed, 57 insertions(+), 12 deletions(-) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 5566d3140..3caac7b35 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -448,6 +448,12 @@ void AIGateway::battleResultsApplied() status.setBattle(NO_BATTLE); } +void AIGateway::beforeObjectPropertyChanged(const SetObjectProperty * sop) +{ + +} + + void AIGateway::objectPropertyChanged(const SetObjectProperty * sop) { LOG_TRACE(logAi); diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index 66f9a323b..a29197b9e 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -161,6 +161,7 @@ public: void heroManaPointsChanged(const CGHeroInstance * hero) override; void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override; void battleResultsApplied() override; + void beforeObjectPropertyChanged(const SetObjectProperty * sop) override; void objectPropertyChanged(const SetObjectProperty * sop) override; void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override; void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 0ec77b099..816d7acfe 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -538,6 +538,11 @@ void VCAI::battleResultsApplied() status.setBattle(NO_BATTLE); } +void VCAI::beforeObjectPropertyChanged(const SetObjectProperty * sop) +{ + +} + void VCAI::objectPropertyChanged(const SetObjectProperty * sop) { LOG_TRACE(logAi); diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 90b6753f3..a932d8702 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -194,6 +194,7 @@ public: void heroManaPointsChanged(const CGHeroInstance * hero) override; void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override; void battleResultsApplied() override; + void beforeObjectPropertyChanged(const SetObjectProperty * sop) override; void objectPropertyChanged(const SetObjectProperty * sop) override; void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override; void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index b40d078b3..c4d180b7d 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1317,11 +1317,8 @@ void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanc GH.windows().createAndPushWindow(hero1, hero2, query); } -void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop) +void CPlayerInterface::beforeObjectPropertyChanged(const SetObjectProperty * sop) { - EVENT_HANDLER_CALLED_BY_CLIENT; - - //redraw minimap if owner changed if (sop->what == ObjProperty::OWNER) { const CGObjectInstance * obj = cb->getObj(sop->id); @@ -1331,13 +1328,34 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop) auto town = static_cast(obj); if(obj->tempOwner == playerID) - localState->addOwnedTown(town); - else + { localState->removeOwnedTown(town); + adventureInt->onTownChanged(town); + } + } + } +} - adventureInt->onTownChanged(town); +void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop) +{ + EVENT_HANDLER_CALLED_BY_CLIENT; + + if (sop->what == ObjProperty::OWNER) + { + const CGObjectInstance * obj = cb->getObj(sop->id); + + if(obj->ID == Obj::TOWN) + { + auto town = static_cast(obj); + + if(obj->tempOwner == playerID) + { + localState->addOwnedTown(town); + adventureInt->onTownChanged(town); + } } + //redraw minimap if owner changed std::set pos = obj->getBlockedPos(); std::unordered_set upos(pos.begin(), pos.end()); adventureInt->onMapTilesChanged(upos); diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index c7273ed6e..877ff2146 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -144,6 +144,7 @@ protected: // Call-ins from server, should not be called directly, but only via void requestRealized(PackageApplied *pa) override; void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override; void centerView (int3 pos, int focusTime) override; + void beforeObjectPropertyChanged(const SetObjectProperty * sop) override; void objectPropertyChanged(const SetObjectProperty * sop) override; void objectRemoved(const CGObjectInstance *obj) override; void objectRemovedAfter() override; diff --git a/client/ClientNetPackVisitors.h b/client/ClientNetPackVisitors.h index 20c51428c..f6cd06bc2 100644 --- a/client/ClientNetPackVisitors.h +++ b/client/ClientNetPackVisitors.h @@ -128,4 +128,5 @@ public: virtual void visitBattleStackMoved(BattleStackMoved & pack) override; virtual void visitBattleAttack(BattleAttack & pack) override; virtual void visitStartAction(StartAction & pack) override; -}; \ No newline at end of file + virtual void visitSetObjectProperty(SetObjectProperty & pack) override; +}; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 1be3c4c13..5c92bebe4 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -611,6 +611,20 @@ void ApplyClientNetPackVisitor::visitInfoWindow(InfoWindow & pack) logNetwork->warn("We received InfoWindow for not our player..."); } +void ApplyFirstClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack) +{ + //inform all players that see this object + for(auto it = cl.playerint.cbegin(); it != cl.playerint.cend(); ++it) + { + if(gs.isVisible(gs.getObjInstance(pack.id), it->first)) + callInterfaceIfPresent(cl, it->first, &IGameEventsReceiver::beforeObjectPropertyChanged, &pack); + } + + // invalidate section of map view with our object and force an update with new flag color + if (pack.what == ObjProperty::OWNER) + CGI->mh->onObjectInstantRemove(gs.getObjInstance(pack.id)); +} + void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack) { //inform all players that see this object @@ -620,12 +634,9 @@ void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack) callInterfaceIfPresent(cl, it->first, &IGameEventsReceiver::objectPropertyChanged, &pack); } + // invalidate section of map view with our object and force an update with new flag color if (pack.what == ObjProperty::OWNER) - { - // invalidate section of map view with our object and force an update with new flag color - CGI->mh->onObjectInstantRemove(gs.getObjInstance(pack.id)); CGI->mh->onObjectInstantAdd(gs.getObjInstance(pack.id)); - } } void ApplyClientNetPackVisitor::visitHeroLevelUp(HeroLevelUp & pack) diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index 226b92198..af2132060 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -127,6 +127,7 @@ public: virtual void playerBonusChanged(const Bonus &bonus, bool gain){};//if gain hero received bonus, else he lost it virtual void requestSent(const CPackForServer *pack, int requestID){}; virtual void requestRealized(PackageApplied *pa){}; + virtual void beforeObjectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged virtual void objectRemoved(const CGObjectInstance *obj){}; //eg. collected resource, picked artifact, beaten hero virtual void objectRemovedAfter(){}; //eg. collected resource, picked artifact, beaten hero From 1dff721747f9e542a5577f3232751cca9ffb167b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 20:03:10 +0300 Subject: [PATCH 10/15] Fix infobox resetting to current hero during enemy turn --- client/adventureMap/AdventureMapInterface.cpp | 25 +++++++++++-------- client/gui/CIntObject.cpp | 4 +-- client/widgets/ObjectLists.cpp | 3 +++ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index a67fc13ed..ff56d489c 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -83,8 +83,11 @@ void AdventureMapInterface::onAudioPaused() void AdventureMapInterface::onHeroMovementStarted(const CGHeroInstance * hero) { - widget->getInfoBar()->popAll(); - widget->getInfoBar()->showSelection(); + if (shortcuts->optionMapViewActive()) + { + widget->getInfoBar()->popAll(); + widget->getInfoBar()->showSelection(); + } } void AdventureMapInterface::onHeroChanged(const CGHeroInstance *h) @@ -134,15 +137,15 @@ void AdventureMapInterface::activate() } void AdventureMapInterface::deactivate() -{ - CIntObject::deactivate(); - CCS->curh->set(Cursor::Map::POINTER); - - if(LOCPLINT) - LOCPLINT->cingconsole->deactivate(); -} - -void AdventureMapInterface::showAll(Canvas & to) +{ + CIntObject::deactivate(); + CCS->curh->set(Cursor::Map::POINTER); + + if(LOCPLINT) + LOCPLINT->cingconsole->deactivate(); +} + +void AdventureMapInterface::showAll(Canvas & to) { CIntObject::showAll(to); LOCPLINT->cingconsole->show(to); diff --git a/client/gui/CIntObject.cpp b/client/gui/CIntObject.cpp index d82dc6dde..ea6d54234 100644 --- a/client/gui/CIntObject.cpp +++ b/client/gui/CIntObject.cpp @@ -37,8 +37,6 @@ CIntObject::~CIntObject() if(isActive()) deactivate(); - GH.events().assertElementInactive(this); - while(!children.empty()) { if((defActions & DISPOSE) && (children.front()->recActions & DISPOSE)) @@ -104,7 +102,7 @@ void CIntObject::deactivate() void CIntObject::addUsedEvents(ui16 newActions) { - if (isActive()) + if (isActive() && inputEnabled) activateEvents(~used & newActions); used |= newActions; } diff --git a/client/widgets/ObjectLists.cpp b/client/widgets/ObjectLists.cpp index e9f873464..421be9d35 100644 --- a/client/widgets/ObjectLists.cpp +++ b/client/widgets/ObjectLists.cpp @@ -138,6 +138,9 @@ void CListBox::reset() void CListBox::resize(size_t newSize) { + if (totalSize == newSize) + return; + totalSize = newSize; if (slider) slider->setAmount((int)totalSize); From 07d9674828b576ef6b73b3db459c5c32b17b7049 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 20:11:16 +0300 Subject: [PATCH 11/15] Mouse click that activates a window will also trigger mouse event --- client/eventsSDL/InputSourceMouse.cpp | 6 ++++++ client/eventsSDL/InputSourceMouse.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/client/eventsSDL/InputSourceMouse.cpp b/client/eventsSDL/InputSourceMouse.cpp index 2d8c0b25b..7d35b6797 100644 --- a/client/eventsSDL/InputSourceMouse.cpp +++ b/client/eventsSDL/InputSourceMouse.cpp @@ -18,6 +18,12 @@ #include "../gui/MouseButton.h" #include +#include + +InputSourceMouse::InputSourceMouse() +{ + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); +} void InputSourceMouse::handleEventMouseMotion(const SDL_MouseMotionEvent & motion) { diff --git a/client/eventsSDL/InputSourceMouse.h b/client/eventsSDL/InputSourceMouse.h index 58699c0bb..7b57e614b 100644 --- a/client/eventsSDL/InputSourceMouse.h +++ b/client/eventsSDL/InputSourceMouse.h @@ -24,6 +24,8 @@ class InputSourceMouse Point middleClickPosition; int mouseButtonsMask = 0; public: + InputSourceMouse(); + void handleEventMouseMotion(const SDL_MouseMotionEvent & current); void handleEventMouseButtonDown(const SDL_MouseButtonEvent & current); void handleEventMouseWheel(const SDL_MouseWheelEvent & current); From b775385179e2cdd46e64f2427693e4337978fb99 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 21:35:26 +0300 Subject: [PATCH 12/15] Fix usage of left/right button to select amounts, e.g. in recruit screen --- client/lobby/SelectionTab.cpp | 2 +- client/widgets/Slider.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 6b85b8f9d..ad2f4c0de 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -460,7 +460,7 @@ void SelectionTab::updateListItems() bool SelectionTab::receiveEvent(const Point & position, int eventType) const { // FIXME: widget should instead have well-defined pos so events will be filtered using standard routine - return getLine(position) != -1; + return getLine(position - pos.topLeft()) != -1; } int SelectionTab::getLine() const diff --git a/client/widgets/Slider.cpp b/client/widgets/Slider.cpp index 8035d1739..97e99120b 100644 --- a/client/widgets/Slider.cpp +++ b/client/widgets/Slider.cpp @@ -214,6 +214,10 @@ CSlider::CSlider(Point position, int totalw, std::function Moved, int pos.h = totalw; } + // for horizontal sliders that act as values selection - add keyboard event to receive left/right click + if (getOrientation() == Orientation::HORIZONTAL) + addUsedEvents(KEYBOARD); + updateSliderPos(); } From 85655d5534589a338b1e0bbd25c8d82ae33eac7d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jul 2023 21:42:37 +0300 Subject: [PATCH 13/15] Block map level toggle on maps without underground --- client/adventureMap/AdventureMapShortcuts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/adventureMap/AdventureMapShortcuts.cpp b/client/adventureMap/AdventureMapShortcuts.cpp index ea5a267c7..40fa7df6a 100644 --- a/client/adventureMap/AdventureMapShortcuts.cpp +++ b/client/adventureMap/AdventureMapShortcuts.cpp @@ -402,7 +402,7 @@ bool AdventureMapShortcuts::optionCanViewQuests() bool AdventureMapShortcuts::optionCanToggleLevel() { - return optionInMapView() && LOCPLINT->cb->getMapSize().z > 0; + return optionInMapView() && LOCPLINT->cb->getMapSize().z > 1; } bool AdventureMapShortcuts::optionMapLevelSurface() From 41755c9d873e157c32a69a5d21895c4add22799d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 7 Jul 2023 01:23:17 +0300 Subject: [PATCH 14/15] Fix slider scroll via UI buttons --- client/widgets/Slider.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/widgets/Slider.cpp b/client/widgets/Slider.cpp index 97e99120b..011d91806 100644 --- a/client/widgets/Slider.cpp +++ b/client/widgets/Slider.cpp @@ -138,6 +138,10 @@ void CSlider::clickLeft(tribool down, bool previousState) rw = pw / (pos.h-48); } + // click on area covered by buttons -> ignore, will be handled by left/right buttons + if (!vstd::iswithin(rw, 0, 1)) + return; + slider->clickLeft(true, slider->isMouseLeftButtonPressed()); scrollTo((int)(rw * positions + 0.5)); return; From a9d25b0109f0660fd3de00c2ab2a448c5fa1543f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 7 Jul 2023 01:27:57 +0300 Subject: [PATCH 15/15] Fix map grid visibility on game load --- client/mapView/MapViewController.cpp | 10 +++++++++- client/mapView/MapViewController.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/mapView/MapViewController.cpp b/client/mapView/MapViewController.cpp index 10b658fb4..7f29987f7 100644 --- a/client/mapView/MapViewController.cpp +++ b/client/mapView/MapViewController.cpp @@ -185,9 +185,16 @@ void MapViewController::tick(uint32_t timeDelta) fadingInContext->progress = std::min( 1.0, fadingInContext->progress); } + if (adventureContext) + adventureContext->animationTime += timeDelta; + + updateState(); +} + +void MapViewController::updateState() +{ if(adventureContext) { - adventureContext->animationTime += timeDelta; adventureContext->settingsSessionSpectate = settings["session"]["spectate"].Bool(); adventureContext->settingsAdventureObjectAnimation = settings["adventure"]["objectAnimation"].Bool(); adventureContext->settingsAdventureTerrainAnimation = settings["adventure"]["terrainAnimation"].Bool(); @@ -511,6 +518,7 @@ void MapViewController::activateAdventureContext(uint32_t animationTime) adventureContext = std::make_shared(*state); adventureContext->animationTime = animationTime; context = adventureContext; + updateState(); } void MapViewController::activateAdventureContext() diff --git a/client/mapView/MapViewController.h b/client/mapView/MapViewController.h index cf09d7c81..7b9941deb 100644 --- a/client/mapView/MapViewController.h +++ b/client/mapView/MapViewController.h @@ -74,6 +74,7 @@ private: void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; void resetContext(); + void updateState(); public: MapViewController(std::shared_ptr model, std::shared_ptr view);