Skip to content

Commit

Permalink
Clear the selected trigger in the windows when the level changes (#755)
Browse files Browse the repository at this point in the history
When the level changes go through all windows that store the selected trigger and clear the selection. This stops them referring to triggers that no longer exist.
Add tests for this in TriggersWindowManager and RoomsWindowManager. Only tests for this specific fix are added for RoomsWindowManager - they will be added separately as they would be too big for this change.
Closes #751
  • Loading branch information
chreden authored Apr 14, 2021
1 parent bb6852a commit 17c0b9d
Show file tree
Hide file tree
Showing 25 changed files with 376 additions and 10 deletions.
33 changes: 33 additions & 0 deletions trview.app.tests/RoomsWindowManagerTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <trview.app/Windows/RoomsWindowManager.h>
#include <trview.app/Elements/Types.h>
#include <trview.app/Mocks/Windows/IRoomsWindow.h>
#include <trview.common/Mocks/Windows/IShortcuts.h>
#include <trview.app/Mocks/Geometry/IMesh.h>

using namespace trview;
using namespace trview::mocks;
using namespace trview::tests;

TEST(RoomsWindowManager, SetTriggersClearsSelectedTrigger)
{
Event<> shortcut_handler;
auto shortcuts = std::make_shared<MockShortcuts>();
EXPECT_CALL(*shortcuts, add_shortcut).WillOnce([&](auto, auto) -> Event<>&{ return shortcut_handler; });
auto mock_window = std::make_shared<MockRoomsWindow>();
EXPECT_CALL(*mock_window, set_triggers).Times(3);
EXPECT_CALL(*mock_window, clear_selected_trigger).Times(2);
RoomsWindowManager manager(create_test_window(L"RoomsWindowManagerTests"), shortcuts, [&mock_window](...) { return mock_window; });

auto created_window = manager.create_window().lock();
ASSERT_NE(created_window, nullptr);
ASSERT_EQ(created_window, mock_window);

auto trigger1 = std::make_unique<Trigger>(100, 55, 100, 200, TriggerInfo{}, [](auto, auto) { return std::make_unique<MockMesh>(); });
manager.set_triggers({ trigger1.get() });

ASSERT_EQ(manager.selected_trigger(), nullptr);
manager.set_selected_trigger(trigger1.get());
ASSERT_EQ(manager.selected_trigger(), trigger1.get());
manager.set_triggers({});
ASSERT_EQ(manager.selected_trigger(), nullptr);
}
127 changes: 127 additions & 0 deletions trview.app.tests/RoomsWindowTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include <trview.app/Windows/RoomsWindow.h>
#include <trview.ui.render/Mocks/IRenderer.h>
#include <trview.ui/Button.h>
#include <trview.ui/Listbox.h>
#include <trview.app/Elements/Types.h>
#include <trview.graphics/mocks/IDeviceWindow.h>
#include <trview.app/Mocks/Geometry/IMesh.h>
#include <trview.ui.render/Mocks/IMapRenderer.h>
#include <trview.app/Mocks/Elements/ILevel.h>
#include <trlevel/Mocks/ILevel.h>
#include <trview.app/Mocks/Graphics/ILevelTextureStorage.h>
#include <trview.app/Mocks/Graphics/IMeshStorage.h>

using namespace trview;
using namespace trview::tests;
using namespace trview::graphics;
using namespace trview::graphics::mocks;
using namespace trview::ui::render::mocks;
using namespace trview::mocks;

TEST(RoomsWindow, ClearSelectedTriggerClearsSelection)
{
auto [renderer_ptr_source, renderer] = create_mock<MockRenderer>();
auto renderer_ptr = std::move(renderer_ptr_source);
RoomsWindow window([&](auto) { return std::make_unique<MockDeviceWindow>(); }, [&](auto) { return std::move(renderer_ptr); },
[&](auto) { return std::make_unique<MockMapRenderer>(); }, create_test_window(L"RoomsWindowTests"));

std::optional<Trigger*> raised_trigger;
auto token = window.on_trigger_selected += [&raised_trigger](const auto& trigger) { raised_trigger = trigger; };

auto [trlevel_ptr, trlevel] = create_mock<trlevel::mocks::MockLevel>();
auto [level_ptr, level] = create_mock<MockLevel>();
auto [texture_storage_ptr, texture_storage] = create_mock<MockLevelTextureStorage>();
auto [mesh_storage_ptr, mesh_storage] = create_mock<MockMeshStorage>();
trlevel::tr3_room tr_room{};

auto room = std::make_unique<Room>(
[](auto, auto, auto, auto, auto) { return std::make_unique<MockMesh>(); },
trlevel, tr_room, texture_storage, mesh_storage, 0, level);

auto trigger1 = std::make_unique<Trigger>(0, 0, 100, 200, TriggerInfo{ 0, 0, 0, TriggerType::Trigger, 0, { } }, [](auto, auto) { return std::make_unique<MockMesh>(); });
room->add_trigger(trigger1.get());

window.set_rooms({ room.get() });
window.set_triggers({ trigger1.get() });

auto list = window.root_control()->find<ui::Listbox>(RoomsWindow::Names::rooms_listbox);
ASSERT_NE(list, nullptr);

auto row = list->find<ui::Control>(ui::Listbox::Names::row_name_format + "0");
ASSERT_NE(row, nullptr);

auto cell = row->find<ui::Button>(ui::Listbox::Row::Names::cell_name_format + "#");
ASSERT_NE(cell, nullptr);
cell->clicked(Point());

auto triggers_list = window.root_control()->find<ui::Listbox>(RoomsWindow::Names::triggers_listbox);
ASSERT_NE(triggers_list, nullptr);

auto triggers_row = triggers_list->find<ui::Control>(ui::Listbox::Names::row_name_format + "0");
ASSERT_NE(triggers_row, nullptr);

auto triggers_cell = triggers_row->find<ui::Button>(ui::Listbox::Row::Names::cell_name_format + "#");
ASSERT_NE(triggers_cell, nullptr);

ASSERT_FALSE(triggers_list->selected_item().has_value());

triggers_cell->clicked(Point());
ASSERT_TRUE(triggers_list->selected_item().has_value());

window.clear_selected_trigger();
ASSERT_FALSE(triggers_list->selected_item().has_value());
}

TEST(RoomsWindow, SetTriggersClearsSelection)
{
auto [renderer_ptr_source, renderer] = create_mock<MockRenderer>();
auto renderer_ptr = std::move(renderer_ptr_source);
RoomsWindow window([&](auto) { return std::make_unique<MockDeviceWindow>(); }, [&](auto) { return std::move(renderer_ptr); },
[&](auto) { return std::make_unique<MockMapRenderer>(); }, create_test_window(L"RoomsWindowTests"));

std::optional<Trigger*> raised_trigger;
auto token = window.on_trigger_selected += [&raised_trigger](const auto& trigger) { raised_trigger = trigger; };

auto [trlevel_ptr, trlevel] = create_mock<trlevel::mocks::MockLevel>();
auto [level_ptr, level] = create_mock<MockLevel>();
auto [texture_storage_ptr, texture_storage] = create_mock<MockLevelTextureStorage>();
auto [mesh_storage_ptr, mesh_storage] = create_mock<MockMeshStorage>();
trlevel::tr3_room tr_room{};

auto room = std::make_unique<Room>(
[](auto, auto, auto, auto, auto) { return std::make_unique<MockMesh>(); },
trlevel, tr_room, texture_storage, mesh_storage, 0, level);

auto trigger1 = std::make_unique<Trigger>(0, 0, 100, 200, TriggerInfo{ 0, 0, 0, TriggerType::Trigger, 0, { } }, [](auto, auto) { return std::make_unique<MockMesh>(); });
room->add_trigger(trigger1.get());

window.set_rooms({ room.get() });
window.set_triggers({ trigger1.get() });

auto list = window.root_control()->find<ui::Listbox>(RoomsWindow::Names::rooms_listbox);
ASSERT_NE(list, nullptr);

auto row = list->find<ui::Control>(ui::Listbox::Names::row_name_format + "0");
ASSERT_NE(row, nullptr);

auto cell = row->find<ui::Button>(ui::Listbox::Row::Names::cell_name_format + "#");
ASSERT_NE(cell, nullptr);
cell->clicked(Point());

auto triggers_list = window.root_control()->find<ui::Listbox>(RoomsWindow::Names::triggers_listbox);
ASSERT_NE(triggers_list, nullptr);

auto triggers_row = triggers_list->find<ui::Control>(ui::Listbox::Names::row_name_format + "0");
ASSERT_NE(triggers_row, nullptr);

auto triggers_cell = triggers_row->find<ui::Button>(ui::Listbox::Row::Names::cell_name_format + "#");
ASSERT_NE(triggers_cell, nullptr);

ASSERT_FALSE(triggers_list->selected_item().has_value());

triggers_cell->clicked(Point());
ASSERT_TRUE(triggers_list->selected_item().has_value());

window.set_triggers({});
ASSERT_FALSE(triggers_list->selected_item().has_value());
}
24 changes: 24 additions & 0 deletions trview.app.tests/TriggersWindowManagerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,30 @@ TEST(TriggersWindowManager, SetTriggersSetsTriggersOnWindows)
manager.set_triggers({ trigger1.get(), trigger2.get() });
}

TEST(TriggersWindowManager, SetTriggersClearsSelectedTrigger)
{
Event<> shortcut_handler;
auto shortcuts = std::make_shared<MockShortcuts>();
EXPECT_CALL(*shortcuts, add_shortcut).WillOnce([&](auto, auto) -> Event<>&{ return shortcut_handler; });
auto mock_window = std::make_shared<MockTriggersWindow>();
EXPECT_CALL(*mock_window, set_triggers).Times(3);
EXPECT_CALL(*mock_window, clear_selected_trigger).Times(2);
TriggersWindowManager manager(create_test_window(L"TriggersWindowManagerTests"), shortcuts, [&mock_window](...) { return mock_window; });

auto created_window = manager.create_window().lock();
ASSERT_NE(created_window, nullptr);
ASSERT_EQ(created_window, mock_window);

auto trigger1 = std::make_unique<Trigger>(100, 55, 100, 200, TriggerInfo{}, [](auto, auto) { return std::make_unique<MockMesh>(); });
manager.set_triggers({ trigger1.get() });

ASSERT_EQ(manager.selected_trigger(), nullptr);
manager.set_selected_trigger(trigger1.get());
ASSERT_EQ(manager.selected_trigger(), trigger1.get());
manager.set_triggers({});
ASSERT_EQ(manager.selected_trigger(), nullptr);
}

TEST(TriggersWindowManager, SetTriggerVisibilityUpdatesWindows)
{
Event<> shortcut_handler;
Expand Down
51 changes: 51 additions & 0 deletions trview.app.tests/TriggersWindowTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ TEST(TriggersWindow, ClearSelectedTriggerClearsSelection)
ASSERT_NE(cell, nullptr);
cell->clicked(Point());

ASSERT_TRUE(list->selected_item().has_value());
ASSERT_TRUE(raised_trigger.has_value());
ASSERT_EQ(raised_trigger.value()->number(), 1);

Expand All @@ -364,6 +365,56 @@ TEST(TriggersWindow, ClearSelectedTriggerClearsSelection)
ASSERT_NE(commands_items.size(), 0);

window.clear_selected_trigger();
ASSERT_FALSE(list->selected_item().has_value());

stats_items = stats_list->items();
ASSERT_EQ(stats_items.size(), 0);

commands_items = commands_list->items();
ASSERT_EQ(commands_items.size(), 0);
}

TEST(TriggersWindow, SetTriggersClearsSelection)
{
auto [renderer_ptr_source, renderer] = create_mock<MockRenderer>();
auto renderer_ptr = std::move(renderer_ptr_source);
TriggersWindow window([&](auto) { return std::make_unique<MockDeviceWindow>(); }, [&](auto) { return std::move(renderer_ptr); }, create_test_window(L"TriggersWindowTests"));

std::optional<Trigger*> raised_trigger;
auto token = window.on_trigger_selected += [&raised_trigger](const auto& trigger) { raised_trigger = trigger; };

auto trigger1 = std::make_unique<Trigger>(0, 55, 100, 200, TriggerInfo{ 0, 0, 0, TriggerType::Trigger, 0, { } }, [](auto, auto) { return std::make_unique<MockMesh>(); });
auto trigger2 = std::make_unique<Trigger>(1, 78, 100, 200, TriggerInfo{ 0, 0, 0, TriggerType::Antipad, 0, { { TriggerCommandType::Camera, 0 } } }, [](auto, auto) { return std::make_unique<MockMesh>(); });
window.set_triggers({ trigger1.get(), trigger2.get() });

auto list = window.root_control()->find<ui::Listbox>(TriggersWindow::Names::triggers_listbox);
ASSERT_NE(list, nullptr);

auto row = list->find<ui::Control>(ui::Listbox::Names::row_name_format + "1");
ASSERT_NE(row, nullptr);

auto cell = row->find<ui::Button>(ui::Listbox::Row::Names::cell_name_format + "#");
ASSERT_NE(cell, nullptr);
cell->clicked(Point());

ASSERT_TRUE(list->selected_item().has_value());
ASSERT_TRUE(raised_trigger.has_value());
ASSERT_EQ(raised_trigger.value()->number(), 1);

auto stats_list = window.root_control()->find<ui::Listbox>(TriggersWindow::Names::stats_listbox);
ASSERT_NE(stats_list, nullptr);

auto stats_items = stats_list->items();
ASSERT_NE(stats_items.size(), 0);

auto commands_list = window.root_control()->find<ui::Listbox>(TriggersWindow::Names::trigger_commands_listbox);
ASSERT_NE(commands_list, nullptr);

auto commands_items = commands_list->items();
ASSERT_NE(commands_items.size(), 0);

window.set_triggers({});
ASSERT_FALSE(list->selected_item().has_value());

stats_items = stats_list->items();
ASSERT_EQ(stats_items.size(), 0);
Expand Down
2 changes: 2 additions & 0 deletions trview.app.tests/trview.app.tests.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
<ClCompile Include="Menus\MenuDetectorTests.cpp" />
<ClCompile Include="OrbitCameraTests.cpp" />
<ClCompile Include="RecentFilesTests.cpp" />
<ClCompile Include="RoomsWindowManagerTests.cpp" />
<ClCompile Include="RoomsWindowTests.cpp" />
<ClCompile Include="SettingsWindowTests.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
Expand Down
6 changes: 6 additions & 0 deletions trview.app.tests/trview.app.tests.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@
<Filter>Windows</Filter>
</ClCompile>
<ClCompile Include="ApplicationTests.cpp" />
<ClCompile Include="RoomsWindowManagerTests.cpp">
<Filter>Windows</Filter>
</ClCompile>
<ClCompile Include="RoomsWindowTests.cpp">
<Filter>Windows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Input">
Expand Down
2 changes: 1 addition & 1 deletion trview.app/Elements/Room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace trview
const ILevelTextureStorage& texture_storage,
const IMeshStorage& mesh_storage,
uint32_t index,
Level& parent_level)
ILevel& parent_level)
: _info { room.info.x, 0, room.info.z, room.info.yBottom, room.info.yTop },
_alternate_room(room.alternate_room),
_alternate_group(room.alternate_group),
Expand Down
6 changes: 3 additions & 3 deletions trview.app/Elements/Room.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace trview
struct ICamera;
class Mesh;
class TransparencyBuffer;
class Level;
struct ILevel;

class Room
{
Expand All @@ -56,7 +56,7 @@ namespace trview
const ILevelTextureStorage& texture_storage,
const IMeshStorage& mesh_storage,
uint32_t index,
Level& parent_level);
ILevel& parent_level);

Room(const Room&) = delete;
Room& operator=(const Room&) = delete;
Expand Down Expand Up @@ -202,6 +202,6 @@ namespace trview

std::unordered_map<uint32_t, Trigger*> _triggers;
uint16_t _flags{ 0 };
Level& _level;
ILevel& _level;
};
}
22 changes: 22 additions & 0 deletions trview.app/Mocks/Windows/IRoomsWindow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <trview.app/Windows/IRoomsWindow.h>

namespace trview
{
namespace mocks
{
struct MockRoomsWindow : public IRoomsWindow
{
virtual ~MockRoomsWindow() = default;
MOCK_METHOD(void, clear_selected_trigger, ());
MOCK_METHOD(void, render, (bool));
MOCK_METHOD(void, set_current_room, (uint32_t));
MOCK_METHOD(void, set_items, (const std::vector<Item>&));
MOCK_METHOD(void, set_rooms, (const std::vector<Room*>&));
MOCK_METHOD(void, set_selected_item, (const Item&));;
MOCK_METHOD(void, set_selected_trigger, (const Trigger* const));
MOCK_METHOD(void, set_triggers, (const std::vector<Trigger*>&));
};
}
}
2 changes: 1 addition & 1 deletion trview.app/Mocks/Windows/IRoomsWindowManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace trview
MOCK_METHOD(void, set_selected_item, (const Item&));
MOCK_METHOD(void, set_selected_trigger, (const Trigger* const ));
MOCK_METHOD(void, set_triggers, (const std::vector<Trigger*>&));
MOCK_METHOD(void, create_window, ());
MOCK_METHOD(std::weak_ptr<IRoomsWindow>, create_window, ());
};
}
}
Expand Down
3 changes: 3 additions & 0 deletions trview.app/Windows/IRoomsWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ namespace trview
/// Event raised when the window is closed.
Event<> on_window_closed;

/// Clear the selected trigger.
virtual void clear_selected_trigger() = 0;

/// Render the window.
/// @param vsync Whether to use vsync or not.
virtual void render(bool vsync) = 0;
Expand Down
2 changes: 1 addition & 1 deletion trview.app/Windows/IRoomsWindowManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ namespace trview
virtual void set_triggers(const std::vector<Trigger*>& triggers) = 0;

/// Create a new rooms window.
virtual void create_window() = 0;
virtual std::weak_ptr<IRoomsWindow> create_window() = 0;
};
}
Loading

0 comments on commit 17c0b9d

Please sign in to comment.