optimize: intrusive list supported

This commit is contained in:
Haibo 2018-11-15 23:40:13 +08:00 committed by Nomango
parent 298f686729
commit eb59f7b245
22 changed files with 401 additions and 166 deletions

View File

@ -21,6 +21,7 @@
#pragma once
#include "base.h"
#include "time.h"
#include "IntrusiveList.hpp"
namespace easy2d
{
@ -28,6 +29,7 @@ namespace easy2d
class Action
: public RefCounter
, public IntrusiveItem<spAction>
{
E2D_DISABLE_COPY(Action);
@ -42,18 +44,18 @@ namespace easy2d
virtual ~Action() {}
// 获取动作运行状态
inline bool IsRunning() { return running_; }
virtual bool IsRunning() { return running_; }
// 继续动作
inline void Resume() { running_ = true; }
virtual void Resume() { running_ = true; }
// 暂停动作
inline void Pause() { running_ = false; }
virtual void Pause() { running_ = false; }
// 停止动作
inline void Stop() { done_ = true; }
virtual void Stop() { done_ = true; }
inline bool IsDone() const { return done_; }
virtual bool IsDone() const { return done_; }
// 获取动作的拷贝
virtual spAction Clone() const = 0;

View File

@ -80,12 +80,6 @@ namespace easy2d
{
Action::Update(target, dt);
if (times_ == total_times_)
{
this->Stop();
return;
}
if (action_)
{
action_->Update(target, dt);
@ -97,6 +91,11 @@ namespace easy2d
Action::Reset();
action_->Reset();
}
if (times_ == total_times_)
{
this->Stop();
}
}
else
{
@ -112,6 +111,11 @@ namespace easy2d
times_ = 0;
}
bool Loop::IsRunning()
{
return Action::IsRunning() && times_ != total_times_;
}
//-------------------------------------------------------
// Sequence
@ -142,7 +146,7 @@ namespace easy2d
{
Action::Update(target, dt);
auto &action = actions_[action_index_];
auto& action = actions_[action_index_];
action->Update(target, dt);
if (action->IsDone())

View File

@ -46,6 +46,8 @@ namespace easy2d
// 重置动作
virtual void Reset() override;
virtual bool IsRunning() override;
protected:
// 初始化动作
virtual void Init(Node* target) override;
@ -66,11 +68,13 @@ namespace easy2d
{
E2D_DISABLE_COPY(Sequence);
using Actions = std::vector<spAction>;
public:
Sequence();
explicit Sequence(
const Actions& actions /* 动作列表 */
Actions const& actions /* 动作列表 */
);
virtual ~Sequence();
@ -113,6 +117,8 @@ namespace easy2d
{
E2D_DISABLE_COPY(Spawn);
using Actions = std::vector<spAction>;
public:
Spawn();

View File

@ -25,58 +25,40 @@ namespace easy2d
{
void ActionManager::UpdateActions(Node* target, Duration const& dt)
{
if (actions_.empty())
if (actions_.IsEmpty())
return;
std::vector<spAction> currActions;
currActions.reserve(actions_.size());
std::copy_if(
actions_.begin(),
actions_.end(),
std::back_inserter(currActions),
[](spAction action) { return action->IsRunning() && !action->IsDone(); }
);
// 遍历所有正在运行的动作
for (const auto& action : currActions)
action->Update(target, dt);
// 清除完成的动作
for (auto iter = actions_.begin(); iter != actions_.end();)
spAction next;
for (auto action = actions_.First(); action; action = next)
{
if ((*iter)->IsDone())
{
iter = actions_.erase(iter);
}
else
{
++iter;
}
next = action->Next();
if (action->IsRunning())
action->Update(target, dt);
if (action->IsDone())
actions_.Remove(action);
}
}
void ActionManager::RunAction(spAction const& action)
void ActionManager::AddAction(spAction const& action)
{
if (!action)
logs::Warningln("Node::RunAction failed, action is nullptr");
if (action)
{
auto iter = std::find(actions_.begin(), actions_.end(), action);
if (iter == actions_.end())
{
action->Start();
actions_.push_back(action);
}
action->Start();
actions_.Append(action);
}
}
void ActionManager::ResumeAllActions()
{
if (actions_.empty())
if (actions_.IsEmpty())
return;
for (const auto& action : actions_)
for (auto& action = actions_.First(); action; action = action->Next())
{
action->Resume();
}
@ -84,10 +66,10 @@ namespace easy2d
void ActionManager::PauseAllActions()
{
if (actions_.empty())
if (actions_.IsEmpty())
return;
for (const auto& action : actions_)
for (auto& action = actions_.First(); action; action = action->Next())
{
action->Pause();
}
@ -95,16 +77,16 @@ namespace easy2d
void ActionManager::StopAllActions()
{
if (actions_.empty())
if (actions_.IsEmpty())
return;
for (const auto& action : actions_)
for (auto& action = actions_.First(); action; action = action->Next())
{
action->Stop();
}
}
const Actions& ActionManager::GetAllActions() const
const ActionManager::Actions& ActionManager::GetAllActions() const
{
return actions_;
}

View File

@ -25,9 +25,11 @@ namespace easy2d
{
class ActionManager
{
using Actions = IntrusiveList<spAction>;
public:
// 执行动作
void RunAction(
void AddAction(
spAction const& action
);
@ -41,7 +43,7 @@ namespace easy2d
void StopAllActions();
// 获取所有动作
const Actions& GetAllActions() const;
Actions const& GetAllActions() const;
protected:
void UpdateActions(Node* target, Duration const& dt);

View File

@ -192,7 +192,7 @@ namespace easy2d
return interval_;
}
const Images& Animation::GetFrames() const
Animation::Images const& Animation::GetFrames() const
{
return frames_;
}

View File

@ -29,6 +29,8 @@ namespace easy2d
{
E2D_DISABLE_COPY(Animation);
using Images = std::vector< spImage >;
public:
Animation();
@ -61,7 +63,7 @@ namespace easy2d
Duration const& GetInterval() const;
// 获取关键帧
const Images& GetFrames() const;
Images const& GetFrames() const;
// 设置每一帧的时间间隔
void SetInterval(

231
core/base/IntrusiveList.hpp Normal file
View File

@ -0,0 +1,231 @@
// Copyright (c) 2016-2018 Easy2D - Nomango
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "macros.h"
#include <functional>
namespace easy2d
{
template <typename T>
class IntrusiveList
{
T first_;
T last_;
int size_;
public:
IntrusiveList() : first_(), last_(), size_(0) {}
~IntrusiveList() { Clear(); }
T const& First() const { return first_; }
T& First() { return first_; }
T const& Last() const { return last_; }
T& Last() { return last_; }
bool IsEmpty() const { return size_ == 0; }
int Size() const { return size_; }
void Append(T child)
{
child->prev_ = last_;
child->next_ = nullptr;
if (first_)
{
last_->next_ = child;
}
else
{
first_ = child;
}
last_ = child;
++size_;
#ifdef E2D_DEBUG
Check();
#endif
}
void Prepend(T child)
{
child->prev_ = nullptr;
child->next_ = first_;
if (first_)
{
first_->prev_ = child;
}
else
{
last_ = child;
}
first_ = child;
++size_;
#ifdef E2D_DEBUG
Check();
#endif
}
void Remove(T child)
{
#ifdef E2D_DEBUG
T tmp = first_;
while (tmp != child)
{
if (tmp == last_)
throw std::logic_error("The node to be removed is not in this list");
tmp = tmp->next_;
}
#endif
if (child->next_)
{
child->next_->prev_ = child->prev_;
}
else
{
last_ = child->prev_;
}
if (child->prev_)
{
child->prev_->next_ = child->next_;
}
else
{
first_ = child->next_;
}
child->prev_ = nullptr;
child->next_ = nullptr;
--size_;
#ifdef E2D_DEBUG
Check();
#endif
}
void Insert(T child, T before)
{
if (before->prev_)
before->prev_->next_ = child;
else
first_ = child;
child->prev_ = before->prev_;
child->next_ = before;
before->prev_ = child;
++size_;
#ifdef E2D_DEBUG
Check();
#endif
}
void Clear()
{
T p = first_;
while (p)
{
T tmp = p;
p = p->next_;
if (tmp)
{
tmp->next_ = nullptr;
tmp->prev_ = nullptr;
}
}
first_ = nullptr;
last_ = nullptr;
size_ = 0;
}
void Sort(std::function<bool(T const&, T const&)> func)
{
#ifdef E2D_DEBUG
Check();
#endif
}
#ifdef E2D_DEBUG
void Check()
{
if (!first_)
return;
int pos = 0;
T p = first_;
T tmp = p;
do
{
tmp = p;
p = p->next_;
++pos;
if (p)
{
if (p->prev_ != tmp)
throw std::logic_error("Check list failed");
}
else
{
if (tmp != last_)
throw std::logic_error("Check list failed");
}
} while (p);
if (pos != size_)
throw std::logic_error("Check list failed");
}
#endif
};
template <typename T>
class IntrusiveItem
{
T prev_;
T next_;
template <typename U>
friend class IntrusiveList;
public:
IntrusiveItem() : prev_(), next_() {}
T const& Prev() const { return prev_; }
T& Prev() { return prev_; }
T const& Next() const { return next_; }
T& Next() { return next_; }
};
}

View File

@ -77,7 +77,7 @@ namespace easy2d
graphics->PushClip(final_matrix_, transform_.size);
}
if (children_.empty())
if (children_.IsEmpty())
{
graphics->SetTransform(final_matrix_);
OnDraw();
@ -87,20 +87,17 @@ namespace easy2d
// ×Ó½ÚµãÅÅÐò
if (dirty_sort_)
{
std::sort(
children_.begin(),
children_.end(),
children_.Sort(
[](spNode const& n1, spNode const& n2) { return n1->GetOrder() < n2->GetOrder(); }
);
dirty_sort_ = false;
}
size_t i;
for (i = 0; i < children_.size(); ++i)
spNode child = children_.First();
for (spNode next; child; child = next)
{
auto child = children_[i];
// 访问 Order 小于零的节点
next = child->Next();
if (child->GetOrder() < 0)
{
child->Visit();
@ -114,9 +111,11 @@ namespace easy2d
graphics->SetTransform(final_matrix_);
OnDraw();
// 访问剩余节点
for (; i < children_.size(); ++i)
children_[i]->Visit();
for (spNode next; child; child = next)
{
next = child->Next();
child->Visit();
}
}
if (clip_enabled_)
@ -127,7 +126,7 @@ namespace easy2d
void Node::Update(Duration const& dt)
{
if (children_.empty())
if (children_.IsEmpty())
{
OnUpdate(dt);
UpdateActions(this, dt);
@ -136,13 +135,13 @@ namespace easy2d
}
else
{
size_t i;
for (i = 0; i < children_.size(); ++i)
// ·ÃÎÊ Order СÓÚÁãµÄ½Úµã
spNode child = children_.First();
for (spNode next; child; child = next)
{
auto child = children_[i];
// 访问 Order 小于零的节点
if (child->GetOrder() < 0)
{
next = child->Next();
child->Update(dt);
}
else
@ -156,9 +155,11 @@ namespace easy2d
UpdateTasks(dt);
UpdateTransform();
// 访问剩余节点
for (; i < children_.size(); ++i)
children_[i]->Update(dt);
for (spNode next; child; child = next)
{
next = child->Next();
child->Update(dt);
}
}
}
@ -171,7 +172,7 @@ namespace easy2d
devices::Graphics::Instance()->DrawGeometry(border_, border_color_, 1.f, 1.5f);
}
for (const auto& child : children_)
for (auto& child = children_.First(); child; child = child->Next())
{
child->DrawBorder();
}
@ -202,7 +203,7 @@ namespace easy2d
devices::Graphics::Instance()->CreateRectGeometry(final_matrix_, transform_.size, &border_)
);
for (auto& child : children_)
for (auto& child = children_.First(); child; child = child->Next())
{
child->dirty_transform_ = true;
}
@ -212,8 +213,12 @@ namespace easy2d
{
if (visible_)
{
for (auto riter = children_.crbegin(); riter != children_.crend(); ++riter)
handled = (*riter)->Dispatch(e, handled);
spNode prev;
for (auto& child = children_.Last(); child; child = prev)
{
prev = child->Prev();
handled = child->Dispatch(e, handled);
}
auto handler = dynamic_cast<MouseEventHandler*>(this);
if (handler)
@ -227,8 +232,12 @@ namespace easy2d
{
if (visible_)
{
for (auto riter = children_.crbegin(); riter != children_.crend(); ++riter)
handled = (*riter)->Dispatch(e, handled);
spNode prev;
for (auto& child = children_.Last(); child; child = prev)
{
prev = child->Prev();
handled = child->Dispatch(e, handled);
}
auto handler = dynamic_cast<KeyEventHandler*>(this);
if (handler)
@ -244,7 +253,7 @@ namespace easy2d
{
display_opacity_ = real_opacity_ * parent_->display_opacity_;
}
for (const auto& child : children_)
for (auto& child = children_.First(); child; child = child->Next())
{
child->UpdateOpacity();
}
@ -547,7 +556,7 @@ namespace easy2d
}
}
children_.push_back(child);
children_.Append(child);
child->SetOrder(order);
child->parent_ = this;
@ -573,14 +582,13 @@ namespace easy2d
return parent_;
}
Nodes Node::GetChildren(const String& name) const
Node::Nodes Node::GetChildren(const String& name) const
{
Nodes children;
size_t hash_code = std::hash<String>{}(name);
for (const auto& child : children_)
for (auto child = children_.First(); child != children_.Last(); child = child->Next())
{
// 不同的名称可能会有相同的 Hash 值,但是先比较 Hash 可以提升搜索速度
if (child->hash_name_ == hash_code && child->name_ == name)
{
children.push_back(child);
@ -593,9 +601,8 @@ namespace easy2d
{
size_t hash_code = std::hash<String>{}(name);
for (const auto& child : children_)
for (auto child = children_.First(); child != children_.Last(); child = child->Next())
{
// 不同的名称可能会有相同的 Hash 值,但是先比较 Hash 可以提升搜索速度
if (child->hash_name_ == hash_code && child->name_ == name)
{
return child;
@ -604,14 +611,14 @@ namespace easy2d
return nullptr;
}
const std::vector<spNode>& Node::GetAllChildren() const
Node::Children const & Node::GetChildren() const
{
return children_;
}
int Node::GetChildrenCount() const
{
return static_cast<int>(children_.size());
return children_.Size();
}
void Node::RemoveFromParent()
@ -627,49 +634,40 @@ namespace easy2d
if (!child)
logs::Warningln("Node::RemoveChild failed, child is nullptr");
if (children_.empty())
if (children_.IsEmpty())
{
return false;
}
if (child)
{
auto iter = std::find(children_.begin(), children_.end(), child);
if (iter != children_.end())
{
children_.erase(iter);
child->parent_ = nullptr;
return true;
}
children_.Remove(child);
return true;
}
return false;
}
void Node::RemoveChildren(const String& child_name)
{
if (children_.empty())
if (children_.IsEmpty())
{
return;
}
size_t hash_code = std::hash<String>{}(child_name);
for (auto iter = children_.begin(); iter != children_.end();)
spNode next;
for (auto& child = children_.First(); child; child = next)
{
if ((*iter)->hash_name_ == hash_code && (*iter)->name_ == child_name)
{
(*iter)->parent_ = nullptr;
iter = children_.erase(iter);
}
else
{
++iter;
}
next = child->Next();
if (child->hash_name_ == hash_code && child->name_ == child_name)
children_.Remove(child);
}
}
void Node::RemoveAllChildren()
{
children_.clear();
children_.Clear();
}
bool Node::ContainsPoint(const Point& point)

View File

@ -21,10 +21,11 @@
#pragma once
#include "base.h"
#include "time.h"
#include "ActionManager.h"
#include "TaskManager.h"
#include "KeyEvent.h"
#include "MouseEvent.h"
#include "ActionManager.h"
#include "TaskManager.h"
#include "IntrusiveList.hpp"
#include "../math/Transform.hpp"
#include "../math/Matrix.hpp"
@ -37,6 +38,7 @@ namespace easy2d
: public RefCounter
, public ActionManager
, public TaskManager
, public IntrusiveItem<spNode>
{
friend class Game;
friend class Scene;
@ -44,6 +46,9 @@ namespace easy2d
E2D_DISABLE_COPY(Node);
using Nodes = std::vector< spNode >;
using Children = IntrusiveList<spNode>;
public:
Node();
@ -310,8 +315,8 @@ namespace easy2d
const String& name
) const;
// 获取所有子节点
const Nodes& GetAllChildren() const;
// 获取全部子节点
Children const& GetChildren() const;
// »ñÈ¡×Ó½ÚµãÊýÁ¿
int GetChildrenCount() const;
@ -372,7 +377,7 @@ namespace easy2d
bool dirty_transform_;
Node* parent_;
Color border_color_;
Nodes children_;
Children children_;
ID2D1Geometry* border_;
math::Transform transform_;
math::Matrix initial_matrix_;

View File

@ -21,6 +21,7 @@
#pragma once
#include "base.h"
#include "time.h"
#include "IntrusiveList.hpp"
#include <functional>
namespace easy2d
@ -30,6 +31,7 @@ namespace easy2d
// 定时任务
class Task
: public RefCounter
, public IntrusiveItem<spTask>
{
friend class TaskManager;

View File

@ -19,25 +19,42 @@
// THE SOFTWARE.
#include "TaskManager.h"
#include "logs.h"
namespace easy2d
{
void TaskManager::UpdateTasks(Duration const& dt)
{
if (tasks_.IsEmpty())
return;
spTask next;
for (auto task = tasks_.First(); task; task = next)
{
next = task->Next();
task->Update(dt);
if (task->stopped_)
tasks_.Remove(task);
}
}
void TaskManager::AddTask(spTask const& task)
{
if (!task)
logs::Warningln("Node::AddTask failed, action is nullptr");
if (task)
{
auto iter = std::find(tasks_.begin(), tasks_.end(), task);
if (iter == tasks_.end())
{
task->Reset();
tasks_.push_back(task);
}
task->Reset();
tasks_.Append(task);
}
}
void TaskManager::StopTasks(const String& name)
{
for (const auto& task : tasks_)
for (auto& task = tasks_.First(); task; task = task->Next())
{
if (task->GetName() == name)
{
@ -48,7 +65,7 @@ namespace easy2d
void TaskManager::StartTasks(const String& name)
{
for (const auto& task : tasks_)
for (auto& task = tasks_.First(); task; task = task->Next())
{
if (task->GetName() == name)
{
@ -59,7 +76,7 @@ namespace easy2d
void TaskManager::RemoveTasks(const String& name)
{
for (const auto& task : tasks_)
for (auto& task = tasks_.First(); task; task = task->Next())
{
if (task->GetName() == name)
{
@ -70,7 +87,7 @@ namespace easy2d
void TaskManager::StopAllTasks()
{
for (const auto& task : tasks_)
for (auto& task = tasks_.First(); task; task = task->Next())
{
task->Stop();
}
@ -78,7 +95,7 @@ namespace easy2d
void TaskManager::StartAllTasks()
{
for (const auto& task : tasks_)
for (auto& task = tasks_.First(); task; task = task->Next())
{
task->Start();
}
@ -86,46 +103,14 @@ namespace easy2d
void TaskManager::RemoveAllTasks()
{
for (const auto& task : tasks_)
for (auto& task = tasks_.First(); task; task = task->Next())
{
task->stopped_ = true;
}
}
const Tasks & TaskManager::GetAllTasks() const
const TaskManager::Tasks & TaskManager::GetAllTasks() const
{
return tasks_;
}
void TaskManager::UpdateTasks(Duration const& dt)
{
if (tasks_.empty())
return;
std::vector<spTask> currTasks;
currTasks.reserve(tasks_.size());
std::copy_if(
tasks_.begin(),
tasks_.end(),
std::back_inserter(currTasks),
[](spTask const& task) { return !task->stopped_; }
);
// 遍历就绪的任务
for (const auto& task : currTasks)
task->Update(dt);
// 清除结束的任务
for (auto iter = tasks_.begin(); iter != tasks_.end();)
{
if ((*iter)->stopped_)
{
iter = tasks_.erase(iter);
}
else
{
++iter;
}
}
}
}

View File

@ -25,6 +25,8 @@ namespace easy2d
{
class TaskManager
{
using Tasks = IntrusiveList<spTask>;
public:
// Ìí¼ÓÈÎÎñ
void AddTask(

View File

@ -81,12 +81,6 @@ namespace easy2d
E2D_DECLARE_NS_SMART_PTR(ui, Button);
E2D_DECLARE_NS_SMART_PTR(ui, Menu);
using Images = ::std::vector< spImage >;
using Nodes = ::std::vector< spNode >;
using Actions = ::std::vector< spAction >;
using Tasks = ::std::vector< spTask >;
template<class Interface>
inline void SafeRelease(Interface*& p)
{

View File

@ -27,7 +27,7 @@
#include <stdexcept>
#ifndef E2D_LOG
# if defined(DEBUG) || defined(_DEBUG)
# ifdef E2D_DEBUG
# define E2D_LOG(format, ...) easy2d::logs::Println(format, ##__VA_ARGS__)
# else
# define E2D_LOG ((void)0)

View File

@ -68,6 +68,14 @@
# define INITGUID
#endif
#if defined(DEBUG) || defined(_DEBUG)
# ifndef E2D_DEBUG
# define E2D_DEBUG
# endif
#else
# undef E2D_DEBUG
#endif
// Windows Header Files
#include <windows.h>

View File

@ -34,6 +34,7 @@
<ClInclude Include="..\..\core\base\Game.h" />
<ClInclude Include="..\..\core\base\Image.h" />
<ClInclude Include="..\..\core\base\Input.h" />
<ClInclude Include="..\..\core\base\IntrusiveList.hpp" />
<ClInclude Include="..\..\core\base\IntrusivePtr.hpp" />
<ClInclude Include="..\..\core\base\KeyEvent.h" />
<ClInclude Include="..\..\core\base\logs.h" />

View File

@ -155,6 +155,9 @@
<ClInclude Include="..\..\core\base\Font.hpp">
<Filter>base</Filter>
</ClInclude>
<ClInclude Include="..\..\core\base\IntrusiveList.hpp">
<Filter>base</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="base">

View File

@ -34,6 +34,7 @@
<ClInclude Include="..\..\core\base\Game.h" />
<ClInclude Include="..\..\core\base\Image.h" />
<ClInclude Include="..\..\core\base\Input.h" />
<ClInclude Include="..\..\core\base\IntrusiveList.hpp" />
<ClInclude Include="..\..\core\base\IntrusivePtr.hpp" />
<ClInclude Include="..\..\core\base\KeyEvent.h" />
<ClInclude Include="..\..\core\base\logs.h" />

View File

@ -155,6 +155,9 @@
<ClInclude Include="..\..\core\base\Font.hpp">
<Filter>base</Filter>
</ClInclude>
<ClInclude Include="..\..\core\base\IntrusiveList.hpp">
<Filter>base</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="base">

View File

@ -34,6 +34,7 @@
<ClInclude Include="..\..\core\base\Game.h" />
<ClInclude Include="..\..\core\base\Image.h" />
<ClInclude Include="..\..\core\base\Input.h" />
<ClInclude Include="..\..\core\base\IntrusiveList.hpp" />
<ClInclude Include="..\..\core\base\IntrusivePtr.hpp" />
<ClInclude Include="..\..\core\base\KeyEvent.h" />
<ClInclude Include="..\..\core\base\logs.h" />

View File

@ -155,6 +155,9 @@
<ClInclude Include="..\..\core\base\Font.hpp">
<Filter>base</Filter>
</ClInclude>
<ClInclude Include="..\..\core\base\IntrusiveList.hpp">
<Filter>base</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="base">