Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
98
libs/drape_frontend/gui/choose_position_mark.cpp
Normal file
98
libs/drape_frontend/gui/choose_position_mark.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#include "drape_frontend/gui/choose_position_mark.hpp"
|
||||
#include "drape_frontend/gui/drape_gui.hpp"
|
||||
|
||||
#include "drape_frontend/batcher_bucket.hpp"
|
||||
|
||||
#include "shaders/programs.hpp"
|
||||
|
||||
#include "drape/utils/vertex_decl.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct ChoosePositionMarkVertex
|
||||
{
|
||||
ChoosePositionMarkVertex(glsl::vec2 const & position, glsl::vec2 const & texCoord)
|
||||
: m_position(position)
|
||||
, m_texCoord(texCoord)
|
||||
{}
|
||||
|
||||
glsl::vec2 m_position;
|
||||
glsl::vec2 m_texCoord;
|
||||
};
|
||||
|
||||
class ChoosePositionMarkHandle : public Handle
|
||||
{
|
||||
using TBase = Handle;
|
||||
|
||||
public:
|
||||
ChoosePositionMarkHandle(uint32_t id, m2::PointF const & pivot) : Handle(id, dp::Center, pivot)
|
||||
{
|
||||
SetIsVisible(true);
|
||||
}
|
||||
|
||||
bool Update(ScreenBase const & screen) override
|
||||
{
|
||||
SetPivot(glsl::ToVec2(m2::PointF(screen.PixelRectIn3d().Center())));
|
||||
return TBase::Update(screen);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
drape_ptr<ShapeRenderer> ChoosePositionMark::Draw(ref_ptr<dp::GraphicsContext> context,
|
||||
ref_ptr<dp::TextureManager> tex) const
|
||||
{
|
||||
dp::TextureManager::SymbolRegion region;
|
||||
tex->GetSymbolRegion("cross_geoposition", region);
|
||||
glsl::vec2 const halfSize = glsl::ToVec2(region.GetPixelSize() * 0.5f);
|
||||
m2::RectF const texRect = region.GetTexRect();
|
||||
|
||||
ASSERT_EQUAL(m_position.m_anchor, dp::Center, ());
|
||||
ChoosePositionMarkVertex vertexes[] = {
|
||||
ChoosePositionMarkVertex(glsl::vec2(-halfSize.x, halfSize.y), glsl::ToVec2(texRect.LeftTop())),
|
||||
ChoosePositionMarkVertex(glsl::vec2(-halfSize.x, -halfSize.y), glsl::ToVec2(texRect.LeftBottom())),
|
||||
ChoosePositionMarkVertex(glsl::vec2(halfSize.x, halfSize.y), glsl::ToVec2(texRect.RightTop())),
|
||||
ChoosePositionMarkVertex(glsl::vec2(halfSize.x, -halfSize.y), glsl::ToVec2(texRect.RightBottom()))};
|
||||
|
||||
auto state = df::CreateRenderState(gpu::Program::TexturingGui, df::DepthLayer::GuiLayer);
|
||||
state.SetColorTexture(region.GetTexture());
|
||||
state.SetDepthTestEnabled(false);
|
||||
state.SetTextureIndex(region.GetTextureIndex());
|
||||
|
||||
dp::AttributeProvider provider(1 /*streamCount*/, 4 /*vertexCount*/);
|
||||
dp::BindingInfo info(2 /*count*/);
|
||||
|
||||
dp::BindingDecl & posDecl = info.GetBindingDecl(0);
|
||||
posDecl.m_attributeName = "a_position";
|
||||
posDecl.m_componentCount = 2;
|
||||
posDecl.m_componentType = gl_const::GLFloatType;
|
||||
posDecl.m_offset = 0;
|
||||
posDecl.m_stride = sizeof(ChoosePositionMarkVertex);
|
||||
|
||||
dp::BindingDecl & texDecl = info.GetBindingDecl(1);
|
||||
texDecl.m_attributeName = "a_colorTexCoords";
|
||||
texDecl.m_componentCount = 2;
|
||||
texDecl.m_componentType = gl_const::GLFloatType;
|
||||
texDecl.m_offset = sizeof(glsl::vec2);
|
||||
texDecl.m_stride = posDecl.m_stride;
|
||||
|
||||
provider.InitStream(0, info, make_ref(&vertexes));
|
||||
|
||||
drape_ptr<dp::OverlayHandle> handle =
|
||||
make_unique_dp<ChoosePositionMarkHandle>(GuiHandleChoosePositionMark, m_position.m_pixelPivot);
|
||||
|
||||
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
|
||||
dp::Batcher batcher(dp::Batcher::IndexPerQuad, dp::Batcher::VertexPerQuad);
|
||||
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
|
||||
dp::SessionGuard guard(context, batcher, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
|
||||
batcher.InsertTriangleStrip(context, state, make_ref(&provider), std::move(handle));
|
||||
|
||||
return renderer;
|
||||
}
|
||||
} // namespace gui
|
||||
14
libs/drape_frontend/gui/choose_position_mark.hpp
Normal file
14
libs/drape_frontend/gui/choose_position_mark.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/shape.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class ChoosePositionMark : public Shape
|
||||
{
|
||||
public:
|
||||
explicit ChoosePositionMark(gui::Position const & position) : Shape(position) {}
|
||||
|
||||
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const;
|
||||
};
|
||||
} // namespace gui
|
||||
141
libs/drape_frontend/gui/compass.cpp
Normal file
141
libs/drape_frontend/gui/compass.cpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#include "drape_frontend/gui/compass.hpp"
|
||||
|
||||
#include "drape_frontend/animation/show_hide_animation.hpp"
|
||||
#include "drape_frontend/batcher_bucket.hpp"
|
||||
#include "drape_frontend/gui/drape_gui.hpp"
|
||||
|
||||
#include "shaders/programs.hpp"
|
||||
|
||||
#include "drape/glsl_func.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
|
||||
#include "drape/utils/vertex_decl.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct CompassVertex
|
||||
{
|
||||
CompassVertex(glsl::vec2 const & position, glsl::vec2 const & texCoord) : m_position(position), m_texCoord(texCoord)
|
||||
{}
|
||||
|
||||
glsl::vec2 m_position;
|
||||
glsl::vec2 m_texCoord;
|
||||
};
|
||||
|
||||
class CompassHandle : public TappableHandle
|
||||
{
|
||||
using TBase = TappableHandle;
|
||||
|
||||
public:
|
||||
CompassHandle(uint32_t id, m2::PointF const & pivot, m2::PointF const & size, Shape::TTapHandler const & tapHandler)
|
||||
: TappableHandle(id, dp::Center, pivot, size)
|
||||
, m_tapHandler(tapHandler)
|
||||
, m_animation(false, 0.25)
|
||||
{}
|
||||
|
||||
void OnTap() override
|
||||
{
|
||||
if (m_tapHandler != nullptr)
|
||||
m_tapHandler();
|
||||
}
|
||||
|
||||
bool Update(ScreenBase const & screen) override
|
||||
{
|
||||
static double constexpr kVisibleTolerance = 0.1;
|
||||
static double constexpr kVisibleStartAngle = math::DegToRad(0.0 + kVisibleTolerance);
|
||||
static double constexpr kVisibleEndAngle = math::DegToRad(360.0 - kVisibleTolerance);
|
||||
|
||||
auto const angle = static_cast<float>(ang::AngleIn2PI(screen.GetAngle()));
|
||||
|
||||
bool isVisiblePrev = IsVisible();
|
||||
bool isVisibleAngle = angle > kVisibleStartAngle && angle < kVisibleEndAngle;
|
||||
|
||||
bool isVisible = isVisibleAngle || (isVisiblePrev && DrapeGui::Instance().IsInUserAction());
|
||||
|
||||
if (isVisible)
|
||||
{
|
||||
m_animation.ShowAnimated();
|
||||
SetIsVisible(true);
|
||||
}
|
||||
else
|
||||
m_animation.HideAnimated();
|
||||
|
||||
if (IsVisible())
|
||||
{
|
||||
TBase::Update(screen);
|
||||
|
||||
glsl::mat4 r = glsl::rotate(glsl::mat4(), angle, glsl::vec3(0.0, 0.0, 1.0));
|
||||
glsl::mat4 m = glsl::translate(glsl::mat4(), glsl::vec3(m_pivot, 0.0));
|
||||
m_params.m_modelView = glsl::transpose(m * r);
|
||||
m_params.m_opacity = static_cast<float>(m_animation.GetT());
|
||||
}
|
||||
|
||||
if (m_animation.IsFinished())
|
||||
SetIsVisible(isVisible);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Shape::TTapHandler m_tapHandler;
|
||||
df::ShowHideAnimation m_animation;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
drape_ptr<ShapeRenderer> Compass::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex,
|
||||
TTapHandler const & tapHandler) const
|
||||
{
|
||||
dp::TextureManager::SymbolRegion region;
|
||||
tex->GetSymbolRegion("compass", region);
|
||||
auto const halfSize = glsl::ToVec2(region.GetPixelSize() * 0.5f);
|
||||
auto const texRect = region.GetTexRect();
|
||||
|
||||
ASSERT_EQUAL(m_position.m_anchor, dp::Center, ());
|
||||
CompassVertex vertexes[] = {CompassVertex(glsl::vec2(-halfSize.x, halfSize.y), glsl::ToVec2(texRect.LeftTop())),
|
||||
CompassVertex(glsl::vec2(-halfSize.x, -halfSize.y), glsl::ToVec2(texRect.LeftBottom())),
|
||||
CompassVertex(glsl::vec2(halfSize.x, halfSize.y), glsl::ToVec2(texRect.RightTop())),
|
||||
CompassVertex(glsl::vec2(halfSize.x, -halfSize.y), glsl::ToVec2(texRect.RightBottom()))};
|
||||
|
||||
auto state = df::CreateRenderState(gpu::Program::TexturingGui, df::DepthLayer::GuiLayer);
|
||||
state.SetColorTexture(region.GetTexture());
|
||||
state.SetDepthTestEnabled(false);
|
||||
state.SetTextureIndex(region.GetTextureIndex());
|
||||
|
||||
dp::AttributeProvider provider(1, 4);
|
||||
dp::BindingInfo info(2);
|
||||
|
||||
dp::BindingDecl & posDecl = info.GetBindingDecl(0);
|
||||
posDecl.m_attributeName = "a_position";
|
||||
posDecl.m_componentCount = 2;
|
||||
posDecl.m_componentType = gl_const::GLFloatType;
|
||||
posDecl.m_offset = 0;
|
||||
posDecl.m_stride = sizeof(CompassVertex);
|
||||
|
||||
dp::BindingDecl & texDecl = info.GetBindingDecl(1);
|
||||
texDecl.m_attributeName = "a_colorTexCoords";
|
||||
texDecl.m_componentCount = 2;
|
||||
texDecl.m_componentType = gl_const::GLFloatType;
|
||||
texDecl.m_offset = sizeof(glsl::vec2);
|
||||
texDecl.m_stride = posDecl.m_stride;
|
||||
|
||||
provider.InitStream(0, info, make_ref(&vertexes));
|
||||
|
||||
drape_ptr<dp::OverlayHandle> handle = make_unique_dp<CompassHandle>(
|
||||
EGuiHandle::GuiHandleCompass, m_position.m_pixelPivot, region.GetPixelSize(), tapHandler);
|
||||
|
||||
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
|
||||
dp::Batcher batcher(dp::Batcher::IndexPerQuad, dp::Batcher::VertexPerQuad);
|
||||
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
|
||||
dp::SessionGuard guard(context, batcher, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
|
||||
batcher.InsertTriangleStrip(context, state, make_ref(&provider), std::move(handle));
|
||||
|
||||
return renderer;
|
||||
}
|
||||
} // namespace gui
|
||||
15
libs/drape_frontend/gui/compass.hpp
Normal file
15
libs/drape_frontend/gui/compass.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/shape.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class Compass : public Shape
|
||||
{
|
||||
public:
|
||||
explicit Compass(gui::Position const & position) : Shape(position) {}
|
||||
|
||||
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex,
|
||||
TTapHandler const & tapHandler) const;
|
||||
};
|
||||
} // namespace gui
|
||||
92
libs/drape_frontend/gui/copyright_label.cpp
Normal file
92
libs/drape_frontend/gui/copyright_label.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "copyright_label.hpp"
|
||||
#include "drape_gui.hpp"
|
||||
#include "gui_text.hpp"
|
||||
|
||||
#include "drape_frontend/animation/opacity_animation.hpp"
|
||||
#include "drape_frontend/batcher_bucket.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr double kCopyrightVisibleTime = 10.0f;
|
||||
constexpr double kCopyrightHideTime = 0.25f;
|
||||
|
||||
class CopyrightHandle : public StaticLabelHandle
|
||||
{
|
||||
using TBase = StaticLabelHandle;
|
||||
|
||||
public:
|
||||
CopyrightHandle(uint32_t id, ref_ptr<dp::TextureManager> textureManager, dp::Anchor anchor, m2::PointF const & pivot,
|
||||
dp::TGlyphs && glyphs)
|
||||
: TBase(id, textureManager, anchor, pivot, std::move(glyphs))
|
||||
{
|
||||
SetIsVisible(true);
|
||||
}
|
||||
|
||||
bool Update(ScreenBase const & screen) override
|
||||
{
|
||||
if (!IsVisible())
|
||||
return false;
|
||||
|
||||
if (!TBase::Update(screen))
|
||||
return false;
|
||||
|
||||
if (!DrapeGui::Instance().IsCopyrightActive())
|
||||
{
|
||||
SetIsVisible(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_animation == nullptr)
|
||||
{
|
||||
m_animation = make_unique_dp<df::OpacityAnimation>(kCopyrightHideTime, kCopyrightVisibleTime, 1.0f, 0.0f);
|
||||
}
|
||||
else if (m_animation->IsFinished())
|
||||
{
|
||||
DrapeGui::Instance().DeactivateCopyright();
|
||||
SetIsVisible(false);
|
||||
}
|
||||
|
||||
m_params.m_opacity = static_cast<float>(m_animation->GetOpacity());
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
drape_ptr<df::OpacityAnimation> m_animation;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
CopyrightLabel::CopyrightLabel(Position const & position) : TBase(position) {}
|
||||
|
||||
drape_ptr<ShapeRenderer> CopyrightLabel::Draw(ref_ptr<dp::GraphicsContext> context,
|
||||
ref_ptr<dp::TextureManager> tex) const
|
||||
{
|
||||
StaticLabel::LabelResult result;
|
||||
auto glyphs = StaticLabel::CacheStaticText("Map data © OpenStreetMap", "", m_position.m_anchor,
|
||||
DrapeGui::GetGuiTextFont(), tex, result);
|
||||
|
||||
dp::AttributeProvider provider(1 /*stream count*/, static_cast<uint32_t>(result.m_buffer.size()));
|
||||
provider.InitStream(0 /*stream index*/, StaticLabel::Vertex::GetBindingInfo(), make_ref(result.m_buffer.data()));
|
||||
|
||||
auto const vertexCount = static_cast<uint32_t>(result.m_buffer.size());
|
||||
ASSERT(vertexCount % dp::Batcher::VertexPerQuad == 0, ());
|
||||
auto const indexCount = dp::Batcher::IndexPerQuad * vertexCount / dp::Batcher::VertexPerQuad;
|
||||
|
||||
drape_ptr<dp::OverlayHandle> handle = make_unique_dp<CopyrightHandle>(GuiHandleCopyright, tex, m_position.m_anchor,
|
||||
m_position.m_pixelPivot, std::move(glyphs));
|
||||
|
||||
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
|
||||
dp::Batcher batcher(indexCount, vertexCount);
|
||||
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
|
||||
using namespace std::placeholders;
|
||||
dp::SessionGuard guard(context, batcher, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
|
||||
batcher.InsertListOfStrip(context, result.m_state, make_ref(&provider), std::move(handle),
|
||||
dp::Batcher::VertexPerQuad);
|
||||
|
||||
return renderer;
|
||||
}
|
||||
} // namespace gui
|
||||
15
libs/drape_frontend/gui/copyright_label.hpp
Normal file
15
libs/drape_frontend/gui/copyright_label.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/shape.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class CopyrightLabel : public Shape
|
||||
{
|
||||
using TBase = Shape;
|
||||
|
||||
public:
|
||||
explicit CopyrightLabel(Position const & position);
|
||||
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const;
|
||||
};
|
||||
} // namespace gui
|
||||
96
libs/drape_frontend/gui/debug_label.cpp
Normal file
96
libs/drape_frontend/gui/debug_label.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include "drape_frontend/gui/debug_label.hpp"
|
||||
|
||||
#include "drape_frontend/gui/drape_gui.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class DebugLabelHandle : public MutableLabelHandle
|
||||
{
|
||||
using TBase = MutableLabelHandle;
|
||||
|
||||
public:
|
||||
DebugLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, ref_ptr<dp::TextureManager> tex,
|
||||
TUpdateDebugLabelFn const & onUpdateFn)
|
||||
: MutableLabelHandle(id, anchor, pivot)
|
||||
, m_onUpdateFn(onUpdateFn)
|
||||
{
|
||||
SetTextureManager(tex);
|
||||
}
|
||||
|
||||
bool Update(ScreenBase const & screen) override
|
||||
{
|
||||
std::string content;
|
||||
bool const isVisible = m_onUpdateFn(screen, content);
|
||||
|
||||
SetIsVisible(isVisible);
|
||||
SetContent(content);
|
||||
|
||||
return TBase::Update(screen);
|
||||
}
|
||||
|
||||
private:
|
||||
TUpdateDebugLabelFn m_onUpdateFn;
|
||||
};
|
||||
|
||||
void AddSymbols(std::string const & str, std::set<char> & symbols)
|
||||
{
|
||||
for (size_t i = 0, sz = str.length(); i < sz; ++i)
|
||||
symbols.insert(str[i]);
|
||||
}
|
||||
|
||||
void DebugInfoLabels::AddLabel(ref_ptr<dp::TextureManager> tex, std::string const & caption,
|
||||
TUpdateDebugLabelFn const & onUpdateFn)
|
||||
{
|
||||
std::string alphabet;
|
||||
std::set<char> symbols;
|
||||
AddSymbols(caption, symbols);
|
||||
AddSymbols("0123456789.-e", symbols);
|
||||
|
||||
alphabet.reserve(symbols.size());
|
||||
for (auto const & ch : symbols)
|
||||
alphabet.push_back(ch);
|
||||
|
||||
MutableLabelDrawer::Params params;
|
||||
params.m_anchor = dp::LeftTop;
|
||||
params.m_alphabet = alphabet;
|
||||
params.m_maxLength = 100;
|
||||
params.m_font = DrapeGui::GetGuiTextFont();
|
||||
params.m_font.m_color = dp::Color(0, 0, 255, 255);
|
||||
params.m_font.m_size *= 1.2;
|
||||
params.m_pivot = m_position.m_pixelPivot;
|
||||
|
||||
#ifdef RENDER_DEBUG_INFO_LABELS
|
||||
uint32_t const id = GuiHandleDebugLabel + m_labelsCount;
|
||||
|
||||
params.m_handleCreator = [id, onUpdateFn, tex](dp::Anchor anchor, m2::PointF const & pivot)
|
||||
{ return make_unique_dp<DebugLabelHandle>(id, anchor, pivot, tex, onUpdateFn); };
|
||||
#endif
|
||||
m_labelsParams.push_back(params);
|
||||
++m_labelsCount;
|
||||
}
|
||||
|
||||
drape_ptr<ShapeRenderer> DebugInfoLabels::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex)
|
||||
{
|
||||
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
|
||||
|
||||
m2::PointF pos = m_position.m_pixelPivot;
|
||||
|
||||
for (auto & params : m_labelsParams)
|
||||
{
|
||||
params.m_pivot.y = pos.y;
|
||||
m2::PointF textSize =
|
||||
MutableLabelDrawer::Draw(context, params, tex, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
|
||||
pos.y += 2 * textSize.y;
|
||||
}
|
||||
|
||||
m_labelsParams.clear();
|
||||
|
||||
return renderer;
|
||||
}
|
||||
} // namespace gui
|
||||
26
libs/drape_frontend/gui/debug_label.hpp
Normal file
26
libs/drape_frontend/gui/debug_label.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/gui_text.hpp"
|
||||
#include "drape_frontend/gui/shape.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
using TUpdateDebugLabelFn = std::function<bool(ScreenBase const & screen, std::string & content)>;
|
||||
|
||||
class DebugInfoLabels : public Shape
|
||||
{
|
||||
public:
|
||||
explicit DebugInfoLabels(gui::Position const & position) : Shape(position) {}
|
||||
|
||||
void AddLabel(ref_ptr<dp::TextureManager> tex, std::string const & caption, TUpdateDebugLabelFn const & onUpdateFn);
|
||||
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex);
|
||||
|
||||
private:
|
||||
std::vector<MutableLabelDrawer::Params> m_labelsParams;
|
||||
uint32_t m_labelsCount = 0;
|
||||
};
|
||||
} // namespace gui
|
||||
73
libs/drape_frontend/gui/drape_gui.cpp
Normal file
73
libs/drape_frontend/gui/drape_gui.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#include "drape_gui.hpp"
|
||||
#include "ruler_helper.hpp"
|
||||
|
||||
#include "drape_frontend/color_constants.hpp"
|
||||
#include "drape_frontend/visual_params.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
df::ColorConstant const kGuiTextColor = "GuiText";
|
||||
|
||||
struct DrapeGui::Impl
|
||||
{
|
||||
RulerHelper m_rulerHelper;
|
||||
};
|
||||
|
||||
DrapeGui::DrapeGui() : m_impl(new Impl()) {}
|
||||
|
||||
DrapeGui & DrapeGui::Instance()
|
||||
{
|
||||
static DrapeGui s_gui;
|
||||
if (!s_gui.m_impl)
|
||||
s_gui.m_impl.reset(new Impl());
|
||||
|
||||
return s_gui;
|
||||
}
|
||||
|
||||
RulerHelper & DrapeGui::GetRulerHelper()
|
||||
{
|
||||
return Instance().GetRulerHelperImpl();
|
||||
}
|
||||
|
||||
dp::FontDecl DrapeGui::GetGuiTextFont()
|
||||
{
|
||||
return {df::GetColorConstant(kGuiTextColor), 14};
|
||||
}
|
||||
|
||||
void DrapeGui::Destroy()
|
||||
{
|
||||
ASSERT(m_impl != nullptr, ());
|
||||
m_impl.reset();
|
||||
}
|
||||
|
||||
void DrapeGui::SetSurfaceSize(m2::PointF const & size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_surfaceSizeMutex);
|
||||
m_surfaceSize = size;
|
||||
}
|
||||
|
||||
m2::PointF DrapeGui::GetSurfaceSize() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_surfaceSizeMutex);
|
||||
return m_surfaceSize;
|
||||
}
|
||||
|
||||
RulerHelper & DrapeGui::GetRulerHelperImpl()
|
||||
{
|
||||
ASSERT(m_impl != nullptr, ());
|
||||
return m_impl->m_rulerHelper;
|
||||
}
|
||||
|
||||
void DrapeGui::ConnectOnCompassTappedHandler(Shape::TTapHandler const & handler)
|
||||
{
|
||||
m_onCompassTappedHandler = handler;
|
||||
}
|
||||
|
||||
void DrapeGui::CallOnCompassTappedHandler()
|
||||
{
|
||||
if (m_onCompassTappedHandler != nullptr)
|
||||
m_onCompassTappedHandler();
|
||||
}
|
||||
} // namespace gui
|
||||
59
libs/drape_frontend/gui/drape_gui.hpp
Normal file
59
libs/drape_frontend/gui/drape_gui.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/compass.hpp"
|
||||
#include "drape_frontend/gui/scale_fps_helper.hpp"
|
||||
#include "drape_frontend/gui/skin.hpp"
|
||||
|
||||
#include "storage/storage_defines.hpp"
|
||||
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class ScreenBase;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class RulerHelper;
|
||||
|
||||
class DrapeGui
|
||||
{
|
||||
public:
|
||||
static DrapeGui & Instance();
|
||||
static RulerHelper & GetRulerHelper();
|
||||
|
||||
static dp::FontDecl GetGuiTextFont();
|
||||
|
||||
void Destroy();
|
||||
void SetSurfaceSize(m2::PointF const & size);
|
||||
m2::PointF GetSurfaceSize() const;
|
||||
|
||||
bool IsInUserAction() const { return m_inUserAction; }
|
||||
void SetInUserAction(bool isInUserAction) { m_inUserAction = isInUserAction; }
|
||||
|
||||
bool IsCopyrightActive() const { return m_isCopyrightActive; }
|
||||
void DeactivateCopyright() { m_isCopyrightActive = false; }
|
||||
|
||||
void ConnectOnCompassTappedHandler(Shape::TTapHandler const & handler);
|
||||
void CallOnCompassTappedHandler();
|
||||
|
||||
ScaleFpsHelper & GetScaleFpsHelper() { return m_scaleFpsHelper; }
|
||||
ScaleFpsHelper const & GetScaleFpsHelper() const { return m_scaleFpsHelper; }
|
||||
|
||||
private:
|
||||
DrapeGui();
|
||||
RulerHelper & GetRulerHelperImpl();
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
bool m_isCopyrightActive = true;
|
||||
|
||||
Shape::TTapHandler m_onCompassTappedHandler;
|
||||
m2::PointF m_surfaceSize;
|
||||
mutable std::mutex m_surfaceSizeMutex;
|
||||
bool m_inUserAction = false;
|
||||
ScaleFpsHelper m_scaleFpsHelper;
|
||||
};
|
||||
} // namespace gui
|
||||
565
libs/drape_frontend/gui/gui_text.cpp
Normal file
565
libs/drape_frontend/gui/gui_text.cpp
Normal file
|
|
@ -0,0 +1,565 @@
|
|||
#include "drape_frontend/gui/gui_text.hpp"
|
||||
|
||||
#include "drape_frontend/batcher_bucket.hpp"
|
||||
#include "drape_frontend/visual_params.hpp"
|
||||
|
||||
#include "shaders/programs.hpp"
|
||||
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include "drape/font_constants.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
namespace
|
||||
{
|
||||
glsl::vec2 GetNormalsAndMask(dp::TextureManager::GlyphRegion const & glyph, float xOffset, float yOffset,
|
||||
float textRatio, std::array<glsl::vec2, 4> & normals,
|
||||
std::array<glsl::vec2, 4> & maskTexCoord)
|
||||
{
|
||||
m2::PointF const pixelSize = glyph.GetPixelSize() * textRatio;
|
||||
m2::RectF const & r = glyph.GetTexRect();
|
||||
|
||||
xOffset *= textRatio;
|
||||
yOffset *= textRatio;
|
||||
|
||||
float const upVector = -pixelSize.y - yOffset;
|
||||
float const bottomVector = -yOffset;
|
||||
|
||||
normals[0] = glsl::vec2(xOffset, bottomVector);
|
||||
normals[1] = glsl::vec2(xOffset, upVector);
|
||||
normals[2] = glsl::vec2(pixelSize.x + xOffset, bottomVector);
|
||||
normals[3] = glsl::vec2(pixelSize.x + xOffset, upVector);
|
||||
|
||||
maskTexCoord[0] = glsl::ToVec2(r.LeftTop());
|
||||
maskTexCoord[1] = glsl::ToVec2(r.LeftBottom());
|
||||
maskTexCoord[2] = glsl::ToVec2(r.RightTop());
|
||||
maskTexCoord[3] = glsl::ToVec2(r.RightBottom());
|
||||
|
||||
return {xOffset, yOffset};
|
||||
}
|
||||
|
||||
void FillCommonDecl(dp::BindingDecl & decl, std::string const & name, uint8_t compCount, uint8_t stride, uint8_t offset)
|
||||
{
|
||||
decl.m_attributeName = name;
|
||||
decl.m_componentCount = compCount;
|
||||
decl.m_componentType = gl_const::GLFloatType;
|
||||
decl.m_stride = stride;
|
||||
decl.m_offset = offset;
|
||||
}
|
||||
|
||||
void FillPositionDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
|
||||
{
|
||||
FillCommonDecl(decl, "a_position", 3, stride, offset);
|
||||
}
|
||||
|
||||
void FillNormalDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
|
||||
{
|
||||
FillCommonDecl(decl, "a_normal", 2, stride, offset);
|
||||
}
|
||||
|
||||
void FillColorDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
|
||||
{
|
||||
FillCommonDecl(decl, "a_colorTexCoord", 2, stride, offset);
|
||||
}
|
||||
|
||||
void FillOutlineDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
|
||||
{
|
||||
FillCommonDecl(decl, "a_outlineColorTexCoord", 2, stride, offset);
|
||||
}
|
||||
|
||||
void FillMaskDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
|
||||
{
|
||||
FillCommonDecl(decl, "a_maskTexCoord", 2, stride, offset);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
dp::BindingInfo const & StaticLabel::Vertex::GetBindingInfo()
|
||||
{
|
||||
static std::unique_ptr<dp::BindingInfo> info;
|
||||
|
||||
if (info == nullptr)
|
||||
{
|
||||
info = std::make_unique<dp::BindingInfo>(5);
|
||||
uint8_t constexpr stride = sizeof(Vertex);
|
||||
uint8_t offset = 0;
|
||||
|
||||
FillPositionDecl(info->GetBindingDecl(0), stride, offset);
|
||||
offset += sizeof(glsl::vec3);
|
||||
FillColorDecl(info->GetBindingDecl(1), stride, offset);
|
||||
offset += sizeof(glsl::vec2);
|
||||
FillOutlineDecl(info->GetBindingDecl(2), stride, offset);
|
||||
offset += sizeof(glsl::vec2);
|
||||
FillNormalDecl(info->GetBindingDecl(3), stride, offset);
|
||||
offset += sizeof(glsl::vec2);
|
||||
FillMaskDecl(info->GetBindingDecl(4), stride, offset);
|
||||
ASSERT_EQUAL(offset + sizeof(glsl::vec2), stride, ());
|
||||
}
|
||||
|
||||
return *info;
|
||||
}
|
||||
|
||||
StaticLabel::LabelResult::LabelResult()
|
||||
: m_state(df::CreateRenderState(gpu::Program::TextStaticOutlinedGui, df::DepthLayer::GuiLayer))
|
||||
{
|
||||
m_state.SetDepthTestEnabled(false);
|
||||
}
|
||||
|
||||
dp::TGlyphs StaticLabel::CacheStaticText(std::string const & text, char const * delimiters, dp::Anchor anchor,
|
||||
dp::FontDecl const & font, ref_ptr<dp::TextureManager> mng,
|
||||
LabelResult & result)
|
||||
{
|
||||
ASSERT(!text.empty(), ());
|
||||
|
||||
auto const textRatio =
|
||||
font.m_size * static_cast<float>(df::VisualParams::Instance().GetVisualScale() / dp::kBaseFontSizePixels);
|
||||
|
||||
dp::TextureManager::TMultilineGlyphsBuffer buffers;
|
||||
auto const shapedLines = mng->ShapeMultilineText(dp::kBaseFontSizePixels, text, delimiters, buffers);
|
||||
|
||||
ASSERT_EQUAL(shapedLines.size(), buffers.size(), ());
|
||||
|
||||
#ifdef DEBUG
|
||||
for (size_t i = 0; i < buffers.size(); ++i)
|
||||
{
|
||||
ASSERT(!buffers[i].empty(), ());
|
||||
ASSERT_EQUAL(buffers[i].size(), shapedLines[i].m_glyphs.size(), ());
|
||||
}
|
||||
|
||||
ref_ptr<dp::Texture> texture = buffers[0][0].GetTexture();
|
||||
for (dp::TextureManager::TGlyphsBuffer const & b : buffers)
|
||||
for (dp::TextureManager::GlyphRegion const & reg : b)
|
||||
ASSERT(texture == reg.GetTexture(), ());
|
||||
#endif
|
||||
|
||||
dp::TextureManager::ColorRegion color;
|
||||
dp::TextureManager::ColorRegion outline;
|
||||
mng->GetColorRegion(font.m_color, color);
|
||||
mng->GetColorRegion(font.m_outlineColor, outline);
|
||||
ASSERT(color.GetTexture() == outline.GetTexture(), ());
|
||||
|
||||
glsl::vec2 colorTex = glsl::ToVec2(color.GetTexRect().Center());
|
||||
glsl::vec2 outlineTex = glsl::ToVec2(outline.GetTexRect().Center());
|
||||
|
||||
buffer_vector<float, 4> lineLengths;
|
||||
lineLengths.reserve(buffers.size());
|
||||
|
||||
buffer_vector<size_t, 4> ranges;
|
||||
ranges.reserve(buffers.size());
|
||||
|
||||
float fullHeight = 0.0;
|
||||
|
||||
buffer_vector<Vertex, 128> & rb = result.m_buffer;
|
||||
for (int i = static_cast<int>(buffers.size()) - 1; i >= 0; --i)
|
||||
{
|
||||
auto const & glyphs = shapedLines[i].m_glyphs;
|
||||
|
||||
dp::TextureManager::TGlyphsBuffer & regions = buffers[i];
|
||||
lineLengths.push_back(0.0f);
|
||||
float & currentLineLength = lineLengths.back();
|
||||
|
||||
float depth = 0.0;
|
||||
glsl::vec2 pen(0.0, -fullHeight);
|
||||
float prevLineHeight = 0.0;
|
||||
for (size_t j = 0; j < regions.size(); ++j)
|
||||
{
|
||||
auto const & glyphMetrics = glyphs[j];
|
||||
|
||||
std::array<glsl::vec2, 4> normals, maskTex;
|
||||
|
||||
dp::TextureManager::GlyphRegion const & glyph = regions[j];
|
||||
glsl::vec2 offsets =
|
||||
GetNormalsAndMask(glyph, glyphMetrics.m_xOffset, glyphMetrics.m_yOffset, textRatio, normals, maskTex);
|
||||
|
||||
glsl::vec3 position = glsl::vec3(0.0, 0.0, depth);
|
||||
|
||||
for (size_t v = 0; v < normals.size(); ++v)
|
||||
rb.emplace_back(position, colorTex, outlineTex, pen + normals[v], maskTex[v]);
|
||||
|
||||
float const advance = glyphMetrics.m_xAdvance * textRatio;
|
||||
prevLineHeight = std::max(prevLineHeight, offsets.y + glyph.GetPixelHeight() * textRatio);
|
||||
|
||||
pen += glsl::vec2(advance, glyphMetrics.m_yAdvance * textRatio);
|
||||
|
||||
depth += 10.0f;
|
||||
if (j == 0)
|
||||
currentLineLength += (glyph.GetPixelSize().x * textRatio + offsets.x);
|
||||
else
|
||||
currentLineLength += advance;
|
||||
|
||||
if (j == regions.size() - 1)
|
||||
currentLineLength += offsets.x;
|
||||
}
|
||||
|
||||
ranges.push_back(rb.size());
|
||||
|
||||
fullHeight += prevLineHeight;
|
||||
}
|
||||
|
||||
float const halfHeight = 0.5f * fullHeight;
|
||||
|
||||
float yOffset = halfHeight;
|
||||
if (anchor & dp::Top)
|
||||
yOffset = fullHeight;
|
||||
else if (anchor & dp::Bottom)
|
||||
yOffset = 0.0f;
|
||||
|
||||
float maxLineLength = 0.0;
|
||||
size_t startIndex = 0;
|
||||
for (size_t i = 0; i < ranges.size(); ++i)
|
||||
{
|
||||
maxLineLength = std::max(lineLengths[i], maxLineLength);
|
||||
float xOffset = -lineLengths[i] / 2.0f;
|
||||
if (anchor & dp::Left)
|
||||
xOffset = 0;
|
||||
else if (anchor & dp::Right)
|
||||
xOffset += xOffset;
|
||||
|
||||
size_t endIndex = ranges[i];
|
||||
for (size_t j = startIndex; j < endIndex; ++j)
|
||||
{
|
||||
rb[j].m_normal = rb[j].m_normal + glsl::vec2(xOffset, yOffset);
|
||||
result.m_boundRect.Add(glsl::ToPoint(rb[j].m_normal));
|
||||
}
|
||||
|
||||
startIndex = endIndex;
|
||||
}
|
||||
|
||||
result.m_state.SetColorTexture(color.GetTexture());
|
||||
result.m_state.SetMaskTexture(buffers[0][0].GetTexture());
|
||||
|
||||
dp::TGlyphs glyphs;
|
||||
for (auto const & line : shapedLines)
|
||||
for (auto const & glyph : line.m_glyphs)
|
||||
glyphs.emplace_back(glyph.m_key);
|
||||
|
||||
base::SortUnique(glyphs);
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
dp::BindingInfo const & MutableLabel::StaticVertex::GetBindingInfo()
|
||||
{
|
||||
static std::unique_ptr<dp::BindingInfo> info;
|
||||
|
||||
if (info == nullptr)
|
||||
{
|
||||
info = std::make_unique<dp::BindingInfo>(3);
|
||||
|
||||
uint8_t constexpr stride = sizeof(StaticVertex);
|
||||
uint8_t offset = 0;
|
||||
|
||||
FillPositionDecl(info->GetBindingDecl(0), stride, offset);
|
||||
offset += sizeof(glsl::vec3);
|
||||
FillColorDecl(info->GetBindingDecl(1), stride, offset);
|
||||
offset += sizeof(glsl::vec2);
|
||||
FillOutlineDecl(info->GetBindingDecl(2), stride, offset);
|
||||
ASSERT_EQUAL(offset + sizeof(glsl::vec2), stride, ());
|
||||
}
|
||||
|
||||
return *info;
|
||||
}
|
||||
|
||||
dp::BindingInfo const & MutableLabel::DynamicVertex::GetBindingInfo()
|
||||
{
|
||||
static std::unique_ptr<dp::BindingInfo> info;
|
||||
|
||||
if (info == nullptr)
|
||||
{
|
||||
info = std::make_unique<dp::BindingInfo>(2, 1);
|
||||
uint8_t constexpr stride = sizeof(DynamicVertex);
|
||||
uint8_t offset = 0;
|
||||
|
||||
FillNormalDecl(info->GetBindingDecl(0), stride, offset);
|
||||
offset += sizeof(glsl::vec2);
|
||||
FillMaskDecl(info->GetBindingDecl(1), stride, offset);
|
||||
ASSERT_EQUAL(offset + sizeof(glsl::vec2), stride, ());
|
||||
}
|
||||
|
||||
return *info;
|
||||
}
|
||||
|
||||
MutableLabel::PrecacheResult::PrecacheResult()
|
||||
: m_state(CreateRenderState(gpu::Program::TextOutlinedGui, df::DepthLayer::GuiLayer))
|
||||
{
|
||||
m_state.SetDepthTestEnabled(false);
|
||||
}
|
||||
|
||||
MutableLabel::MutableLabel(dp::Anchor anchor) : m_anchor(anchor) {}
|
||||
|
||||
void MutableLabel::SetMaxLength(uint16_t maxLength)
|
||||
{
|
||||
m_maxLength = maxLength;
|
||||
}
|
||||
|
||||
dp::TGlyphs MutableLabel::GetGlyphs() const
|
||||
{
|
||||
dp::TGlyphs glyphs;
|
||||
glyphs.reserve(m_shapedText.m_glyphs.size());
|
||||
|
||||
for (auto const & glyph : m_shapedText.m_glyphs)
|
||||
glyphs.emplace_back(glyph.m_key);
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
void MutableLabel::Precache(PrecacheParams const & params, PrecacheResult & result, ref_ptr<dp::TextureManager> mng)
|
||||
{
|
||||
SetMaxLength(static_cast<uint16_t>(params.m_maxLength));
|
||||
|
||||
m_textRatio = params.m_font.m_size * static_cast<float>(df::VisualParams::Instance().GetVisualScale()) /
|
||||
dp::kBaseFontSizePixels;
|
||||
|
||||
// TODO(AB): Is this shaping/precaching really needed if the text changes every frame?
|
||||
m_shapedText = mng->ShapeSingleTextLine(dp::kBaseFontSizePixels, params.m_alphabet, &m_glyphRegions);
|
||||
|
||||
auto const firstTexture = m_glyphRegions.front().GetTexture();
|
||||
#ifdef DEBUG
|
||||
for (auto const & region : m_glyphRegions)
|
||||
ASSERT_EQUAL(firstTexture, region.GetTexture(), ());
|
||||
#endif
|
||||
result.m_state.SetMaskTexture(firstTexture);
|
||||
|
||||
dp::TextureManager::ColorRegion color;
|
||||
dp::TextureManager::ColorRegion outlineColor;
|
||||
|
||||
mng->GetColorRegion(params.m_font.m_color, color);
|
||||
mng->GetColorRegion(params.m_font.m_outlineColor, outlineColor);
|
||||
result.m_state.SetColorTexture(color.GetTexture());
|
||||
|
||||
glsl::vec2 colorTex = glsl::ToVec2(color.GetTexRect().Center());
|
||||
glsl::vec2 outlineTex = glsl::ToVec2(outlineColor.GetTexRect().Center());
|
||||
|
||||
auto const vertexCount = m_maxLength * dp::Batcher::VertexPerQuad;
|
||||
result.m_buffer.resize(vertexCount, StaticVertex(glsl::vec3(0.0, 0.0, 0.0), colorTex, outlineTex));
|
||||
|
||||
float depth = 0.0f;
|
||||
for (size_t i = 0; i < vertexCount; i += 4)
|
||||
{
|
||||
result.m_buffer[i + 0].m_position.z = depth;
|
||||
result.m_buffer[i + 1].m_position.z = depth;
|
||||
result.m_buffer[i + 2].m_position.z = depth;
|
||||
result.m_buffer[i + 3].m_position.z = depth;
|
||||
depth += 10.0f;
|
||||
}
|
||||
|
||||
result.m_maxPixelSize = m2::PointF(m_shapedText.m_lineWidthInPixels, m_shapedText.m_maxLineHeightInPixels);
|
||||
}
|
||||
|
||||
void MutableLabel::SetText(LabelResult & result, std::string text, ref_ptr<dp::TextureManager> mng)
|
||||
{
|
||||
if (size_t const sz = text.size(); sz < m_maxLength)
|
||||
{
|
||||
/// @todo I don't see a better way to clear cached vertices from the previous frame (text value).
|
||||
text.append(m_maxLength - sz, ' ');
|
||||
}
|
||||
else if (sz > m_maxLength)
|
||||
{
|
||||
text.erase(m_maxLength - 3);
|
||||
text.append("...");
|
||||
}
|
||||
|
||||
// TODO(AB): Calculate only the length for pre-cached glyphs in a simpler way?
|
||||
m_glyphRegions.clear();
|
||||
m_shapedText = mng->ShapeSingleTextLine(dp::kBaseFontSizePixels, text, &m_glyphRegions);
|
||||
|
||||
// TODO(AB): Reuse pre-calculated width and height?
|
||||
// float maxHeight = m_shapedText.m_maxLineHeightInPixels;
|
||||
// float length = m_shapedText.m_lineWidthInPixels;
|
||||
|
||||
float maxHeight = 0.0f;
|
||||
|
||||
std::pair minMaxXPos = {std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest()};
|
||||
float offsetLeft = 0;
|
||||
|
||||
glsl::vec2 pen = glsl::vec2(0.0, 0.0);
|
||||
|
||||
ASSERT_EQUAL(m_glyphRegions.size(), m_shapedText.m_glyphs.size(), ());
|
||||
for (size_t i = 0; i < m_glyphRegions.size(); ++i)
|
||||
{
|
||||
std::array<glsl::vec2, 4> normals, maskTex;
|
||||
dp::TextureManager::GlyphRegion const & glyph = m_glyphRegions[i];
|
||||
auto const & metrics = m_shapedText.m_glyphs[i];
|
||||
glsl::vec2 const offsets =
|
||||
GetNormalsAndMask(glyph, metrics.m_xOffset, metrics.m_yOffset, m_textRatio, normals, maskTex);
|
||||
|
||||
ASSERT_EQUAL(normals.size(), maskTex.size(), ());
|
||||
|
||||
for (size_t j = 0; j < normals.size(); ++j)
|
||||
{
|
||||
result.m_buffer.emplace_back(pen + normals[j], maskTex[j]);
|
||||
auto const & back = result.m_buffer.back();
|
||||
if (back.m_normal.x < minMaxXPos.first)
|
||||
{
|
||||
minMaxXPos.first = back.m_normal.x;
|
||||
offsetLeft = offsets.x;
|
||||
}
|
||||
minMaxXPos.second = std::max(minMaxXPos.second, back.m_normal.x);
|
||||
}
|
||||
|
||||
// TODO(AB): yAdvance is always zero for horizontal layouts.
|
||||
pen += glsl::vec2(metrics.m_xAdvance * m_textRatio, metrics.m_yAdvance * m_textRatio);
|
||||
maxHeight = std::max(maxHeight, offsets.y + glyph.GetPixelHeight() * m_textRatio);
|
||||
}
|
||||
|
||||
float const length = minMaxXPos.second - minMaxXPos.first;
|
||||
// "- offset_left" is an approximation
|
||||
// A correct version should be
|
||||
// "- (offset_first_symbol_from_left + offset_last_symbol_from_right) / 2"
|
||||
// But there is no possibility to determine the offset of the last symbol from the right.
|
||||
// We only have x_offset which is "offset from left"
|
||||
glsl::vec2 anchorModifier = glsl::vec2(-length / 2.0f - offsetLeft, maxHeight / 2.0f);
|
||||
if (m_anchor & dp::Right)
|
||||
anchorModifier.x = -length;
|
||||
else if (m_anchor & dp::Left)
|
||||
anchorModifier.x = 0;
|
||||
|
||||
if (m_anchor & dp::Top)
|
||||
anchorModifier.y = maxHeight;
|
||||
else if (m_anchor & dp::Bottom)
|
||||
anchorModifier.y = 0;
|
||||
|
||||
for (DynamicVertex & v : result.m_buffer)
|
||||
{
|
||||
v.m_normal += anchorModifier;
|
||||
result.m_boundRect.Add(glsl::ToPoint(v.m_normal));
|
||||
}
|
||||
}
|
||||
|
||||
MutableLabelHandle::MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot)
|
||||
: TBase(id, anchor, pivot)
|
||||
, m_textView(make_unique_dp<MutableLabel>(anchor))
|
||||
, m_isContentDirty(true)
|
||||
, m_glyphsReady(false)
|
||||
{}
|
||||
|
||||
MutableLabelHandle::MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot,
|
||||
ref_ptr<dp::TextureManager> textures)
|
||||
: MutableLabelHandle(id, anchor, pivot)
|
||||
{
|
||||
m_textureManager = std::move(textures);
|
||||
}
|
||||
|
||||
void MutableLabelHandle::GetAttributeMutation(ref_ptr<dp::AttributeBufferMutator> mutator) const
|
||||
{
|
||||
if (!m_isContentDirty)
|
||||
return;
|
||||
|
||||
m_isContentDirty = false;
|
||||
MutableLabel::LabelResult result;
|
||||
m_textView->SetText(result, m_content, m_textureManager);
|
||||
|
||||
size_t const byteCount = result.m_buffer.size() * sizeof(MutableLabel::DynamicVertex);
|
||||
auto const dataPointer = static_cast<MutableLabel::DynamicVertex *>(mutator->AllocateMutationBuffer(byteCount));
|
||||
std::copy(result.m_buffer.begin(), result.m_buffer.end(), dataPointer);
|
||||
|
||||
dp::BindingInfo const & binding = MutableLabel::DynamicVertex::GetBindingInfo();
|
||||
auto const & node = GetOffsetNode(binding.GetID());
|
||||
ASSERT_EQUAL(node.first.GetElementSize(), sizeof(MutableLabel::DynamicVertex), ());
|
||||
ASSERT_EQUAL(node.second.m_count, result.m_buffer.size(), ());
|
||||
|
||||
dp::MutateNode mutateNode;
|
||||
mutateNode.m_data = make_ref(dataPointer);
|
||||
mutateNode.m_region = node.second;
|
||||
mutator->AddMutation(node.first, mutateNode);
|
||||
}
|
||||
|
||||
bool MutableLabelHandle::Update(ScreenBase const & screen)
|
||||
{
|
||||
if (!m_glyphsReady)
|
||||
m_glyphsReady = m_textureManager->AreGlyphsReady(m_textView->GetGlyphs());
|
||||
|
||||
if (!m_glyphsReady)
|
||||
return false;
|
||||
|
||||
return TBase::Update(screen);
|
||||
}
|
||||
|
||||
void MutableLabelHandle::SetTextureManager(ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
m_textureManager = textures;
|
||||
}
|
||||
|
||||
ref_ptr<MutableLabel> MutableLabelHandle::GetTextView() const
|
||||
{
|
||||
return make_ref(m_textView);
|
||||
}
|
||||
|
||||
void MutableLabelHandle::SetContent(std::string && content)
|
||||
{
|
||||
if (m_content != content)
|
||||
{
|
||||
m_isContentDirty = true;
|
||||
m_content = std::move(content);
|
||||
}
|
||||
}
|
||||
|
||||
void MutableLabelHandle::SetContent(std::string const & content)
|
||||
{
|
||||
if (m_content != content)
|
||||
{
|
||||
m_isContentDirty = true;
|
||||
m_content = content;
|
||||
}
|
||||
}
|
||||
|
||||
m2::PointF MutableLabelDrawer::Draw(ref_ptr<dp::GraphicsContext> context, Params const & params,
|
||||
ref_ptr<dp::TextureManager> mng, dp::Batcher::TFlushFn && flushFn)
|
||||
{
|
||||
uint32_t const vertexCount = dp::Batcher::VertexPerQuad * params.m_maxLength;
|
||||
uint32_t const indexCount = dp::Batcher::IndexPerQuad * params.m_maxLength;
|
||||
|
||||
ASSERT(params.m_handleCreator != nullptr, ());
|
||||
drape_ptr<MutableLabelHandle> handle = params.m_handleCreator(params.m_anchor, params.m_pivot);
|
||||
|
||||
MutableLabel::PrecacheParams preCacheP;
|
||||
preCacheP.m_alphabet = params.m_alphabet;
|
||||
preCacheP.m_font = params.m_font;
|
||||
preCacheP.m_maxLength = params.m_maxLength;
|
||||
|
||||
MutableLabel::PrecacheResult staticData;
|
||||
|
||||
handle->GetTextView()->Precache(preCacheP, staticData, mng);
|
||||
|
||||
ASSERT_EQUAL(vertexCount, staticData.m_buffer.size(), ());
|
||||
buffer_vector<MutableLabel::DynamicVertex, 128> dynData;
|
||||
dynData.resize(staticData.m_buffer.size());
|
||||
|
||||
dp::BindingInfo const & sBinding = MutableLabel::StaticVertex::GetBindingInfo();
|
||||
dp::BindingInfo const & dBinding = MutableLabel::DynamicVertex::GetBindingInfo();
|
||||
dp::AttributeProvider provider(2 /*stream count*/, static_cast<uint32_t>(staticData.m_buffer.size()));
|
||||
provider.InitStream(0 /*stream index*/, sBinding, make_ref(staticData.m_buffer.data()));
|
||||
provider.InitStream(1 /*stream index*/, dBinding, make_ref(dynData.data()));
|
||||
|
||||
{
|
||||
dp::Batcher batcher(indexCount, vertexCount);
|
||||
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
|
||||
dp::SessionGuard const guard(context, batcher, std::move(flushFn));
|
||||
batcher.InsertListOfStrip(context, staticData.m_state, make_ref(&provider), std::move(handle),
|
||||
dp::Batcher::VertexPerQuad);
|
||||
}
|
||||
|
||||
return staticData.m_maxPixelSize;
|
||||
}
|
||||
|
||||
StaticLabelHandle::StaticLabelHandle(uint32_t id, ref_ptr<dp::TextureManager> textureManager, dp::Anchor anchor,
|
||||
m2::PointF const & pivot, dp::TGlyphs && glyphs)
|
||||
: TBase(id, anchor, pivot)
|
||||
, m_glyphs(std::move(glyphs))
|
||||
, m_textureManager(std::move(textureManager))
|
||||
, m_glyphsReady(false)
|
||||
{}
|
||||
|
||||
bool StaticLabelHandle::Update(ScreenBase const & screen)
|
||||
{
|
||||
if (!m_glyphsReady)
|
||||
m_glyphsReady = m_textureManager->AreGlyphsReady(m_glyphs);
|
||||
|
||||
if (!m_glyphsReady)
|
||||
return false;
|
||||
|
||||
return TBase::Update(screen);
|
||||
}
|
||||
} // namespace gui
|
||||
195
libs/drape_frontend/gui/gui_text.hpp
Normal file
195
libs/drape_frontend/gui/gui_text.hpp
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/shape.hpp"
|
||||
|
||||
#include "base/buffer_vector.hpp"
|
||||
|
||||
#include "drape/binding_info.hpp"
|
||||
#include "drape/drape_global.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
#include "drape/texture_manager.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
using TAlphabet = std::unordered_set<strings::UniChar>;
|
||||
|
||||
class StaticLabel
|
||||
{
|
||||
public:
|
||||
struct Vertex
|
||||
{
|
||||
Vertex() = default;
|
||||
Vertex(glsl::vec3 const & pos, glsl::vec2 const & color, glsl::vec2 const & outline, glsl::vec2 const & normal,
|
||||
glsl::vec2 const & mask)
|
||||
: m_position(pos)
|
||||
, m_colorTexCoord(color)
|
||||
, m_outlineColorTexCoord(outline)
|
||||
, m_normal(normal)
|
||||
, m_maskTexCoord(mask)
|
||||
{}
|
||||
|
||||
static dp::BindingInfo const & GetBindingInfo();
|
||||
|
||||
glsl::vec3 m_position;
|
||||
glsl::vec2 m_colorTexCoord;
|
||||
glsl::vec2 m_outlineColorTexCoord;
|
||||
glsl::vec2 m_normal;
|
||||
glsl::vec2 m_maskTexCoord;
|
||||
};
|
||||
|
||||
struct LabelResult
|
||||
{
|
||||
LabelResult();
|
||||
|
||||
dp::RenderState m_state;
|
||||
buffer_vector<Vertex, 128> m_buffer;
|
||||
m2::RectF m_boundRect;
|
||||
TAlphabet m_alphabet;
|
||||
};
|
||||
|
||||
static dp::TGlyphs CacheStaticText(std::string const & text, char const * delim, dp::Anchor anchor,
|
||||
dp::FontDecl const & font, ref_ptr<dp::TextureManager> mng, LabelResult & result);
|
||||
};
|
||||
|
||||
class MutableLabel
|
||||
{
|
||||
public:
|
||||
struct StaticVertex
|
||||
{
|
||||
StaticVertex() = default;
|
||||
StaticVertex(glsl::vec3 const & position, glsl::vec2 const & color, glsl::vec2 const & outlineColor)
|
||||
: m_position(position)
|
||||
, m_color(color)
|
||||
, m_outline(outlineColor)
|
||||
{}
|
||||
|
||||
static dp::BindingInfo const & GetBindingInfo();
|
||||
|
||||
glsl::vec3 m_position;
|
||||
glsl::vec2 m_color;
|
||||
glsl::vec2 m_outline;
|
||||
};
|
||||
|
||||
struct DynamicVertex
|
||||
{
|
||||
DynamicVertex() = default;
|
||||
DynamicVertex(glsl::vec2 const & normal, glsl::vec2 const & mask) : m_normal(normal), m_maskTexCoord(mask) {}
|
||||
|
||||
static dp::BindingInfo const & GetBindingInfo();
|
||||
|
||||
glsl::vec2 m_normal;
|
||||
glsl::vec2 m_maskTexCoord;
|
||||
};
|
||||
|
||||
explicit MutableLabel(dp::Anchor anchor);
|
||||
|
||||
struct PrecacheParams
|
||||
{
|
||||
std::string m_alphabet;
|
||||
size_t m_maxLength;
|
||||
dp::FontDecl m_font;
|
||||
};
|
||||
|
||||
struct PrecacheResult
|
||||
{
|
||||
PrecacheResult();
|
||||
|
||||
dp::RenderState m_state;
|
||||
buffer_vector<StaticVertex, 128> m_buffer;
|
||||
m2::PointF m_maxPixelSize;
|
||||
};
|
||||
|
||||
struct LabelResult
|
||||
{
|
||||
buffer_vector<DynamicVertex, 128> m_buffer;
|
||||
m2::RectF m_boundRect;
|
||||
};
|
||||
|
||||
void Precache(PrecacheParams const & params, PrecacheResult & result, ref_ptr<dp::TextureManager> mng);
|
||||
|
||||
void SetText(LabelResult & result, std::string text, ref_ptr<dp::TextureManager> mng);
|
||||
|
||||
// TODO(AB): Refactor.
|
||||
dp::TGlyphs GetGlyphs() const;
|
||||
|
||||
private:
|
||||
void SetMaxLength(uint16_t maxLength);
|
||||
|
||||
private:
|
||||
dp::Anchor m_anchor;
|
||||
uint16_t m_maxLength = 0;
|
||||
float m_textRatio = 0.0f;
|
||||
|
||||
dp::text::TextMetrics m_shapedText;
|
||||
dp::TextureManager::TGlyphsBuffer m_glyphRegions;
|
||||
};
|
||||
|
||||
class MutableLabelHandle : public Handle
|
||||
{
|
||||
using TBase = Handle;
|
||||
|
||||
public:
|
||||
MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot);
|
||||
|
||||
MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, ref_ptr<dp::TextureManager> textures);
|
||||
|
||||
void GetAttributeMutation(ref_ptr<dp::AttributeBufferMutator> mutator) const override;
|
||||
|
||||
bool Update(ScreenBase const & screen) override;
|
||||
|
||||
ref_ptr<MutableLabel> GetTextView() const;
|
||||
|
||||
protected:
|
||||
void SetContent(std::string && content);
|
||||
void SetContent(std::string const & content);
|
||||
void SetTextureManager(ref_ptr<dp::TextureManager> textures);
|
||||
|
||||
private:
|
||||
drape_ptr<MutableLabel> m_textView;
|
||||
mutable bool m_isContentDirty;
|
||||
std::string m_content;
|
||||
ref_ptr<dp::TextureManager> m_textureManager;
|
||||
bool m_glyphsReady;
|
||||
};
|
||||
|
||||
class MutableLabelDrawer
|
||||
{
|
||||
public:
|
||||
using TCreatoreResult = drape_ptr<MutableLabelHandle>;
|
||||
using THandleCreator = std::function<TCreatoreResult(dp::Anchor, m2::PointF const & /* pivot */)>;
|
||||
|
||||
struct Params
|
||||
{
|
||||
dp::Anchor m_anchor;
|
||||
dp::FontDecl m_font;
|
||||
m2::PointF m_pivot;
|
||||
std::string m_alphabet;
|
||||
uint32_t m_maxLength;
|
||||
THandleCreator m_handleCreator;
|
||||
};
|
||||
|
||||
// Return maximum pixel size.
|
||||
static m2::PointF Draw(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<dp::TextureManager> mng,
|
||||
dp::Batcher::TFlushFn && flushFn);
|
||||
};
|
||||
|
||||
class StaticLabelHandle : public Handle
|
||||
{
|
||||
using TBase = Handle;
|
||||
|
||||
public:
|
||||
StaticLabelHandle(uint32_t id, ref_ptr<dp::TextureManager> textureManager, dp::Anchor anchor,
|
||||
m2::PointF const & pivot, dp::TGlyphs && glyphs);
|
||||
|
||||
bool Update(ScreenBase const & screen) override;
|
||||
|
||||
private:
|
||||
dp::TGlyphs m_glyphs;
|
||||
ref_ptr<dp::TextureManager> m_textureManager;
|
||||
bool m_glyphsReady;
|
||||
};
|
||||
} // namespace gui
|
||||
378
libs/drape_frontend/gui/layer_render.cpp
Normal file
378
libs/drape_frontend/gui/layer_render.cpp
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
#include "drape_frontend/gui/layer_render.hpp"
|
||||
#include "drape_frontend/gui/choose_position_mark.hpp"
|
||||
#include "drape_frontend/gui/compass.hpp"
|
||||
#include "drape_frontend/gui/copyright_label.hpp"
|
||||
#include "drape_frontend/gui/debug_label.hpp"
|
||||
#include "drape_frontend/gui/drape_gui.hpp"
|
||||
#include "drape_frontend/gui/gui_text.hpp"
|
||||
#include "drape_frontend/gui/ruler.hpp"
|
||||
#include "drape_frontend/gui/ruler_helper.hpp"
|
||||
|
||||
#include "drape_frontend/visual_params.hpp"
|
||||
|
||||
#include "drape/batcher.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/render_bucket.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
LayerRenderer::~LayerRenderer()
|
||||
{
|
||||
DestroyRenderers();
|
||||
}
|
||||
|
||||
void LayerRenderer::Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng)
|
||||
{
|
||||
for (auto & r : m_renderers)
|
||||
r.second->Build(context, mng);
|
||||
}
|
||||
|
||||
void LayerRenderer::Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, bool routingActive,
|
||||
ScreenBase const & screen)
|
||||
{
|
||||
if (HasWidget(gui::WIDGET_RULER))
|
||||
{
|
||||
DrapeGui::GetRulerHelper().ResetTextDirtyFlag();
|
||||
DrapeGui::GetRulerHelper().Update(screen);
|
||||
}
|
||||
|
||||
for (auto & r : m_renderers)
|
||||
{
|
||||
if (routingActive && (r.first == gui::WIDGET_COMPASS || r.first == gui::WIDGET_RULER))
|
||||
continue;
|
||||
|
||||
r.second->Render(context, mng, screen);
|
||||
}
|
||||
}
|
||||
|
||||
void LayerRenderer::Merge(ref_ptr<LayerRenderer> other)
|
||||
{
|
||||
bool activeOverlayFound = false;
|
||||
for (auto & r : other->m_renderers)
|
||||
{
|
||||
auto const it = m_renderers.find(r.first);
|
||||
if (it != m_renderers.end())
|
||||
{
|
||||
auto newActiveOverlay = r.second->FindHandle(m_activeOverlayId);
|
||||
bool const updateActive = (m_activeOverlay != nullptr && newActiveOverlay != nullptr);
|
||||
it->second = std::move(r.second);
|
||||
if (!activeOverlayFound && updateActive)
|
||||
{
|
||||
activeOverlayFound = true;
|
||||
m_activeOverlay = newActiveOverlay;
|
||||
if (m_activeOverlay != nullptr)
|
||||
m_activeOverlay->OnTapBegin();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_renderers.insert(std::make_pair(r.first, std::move(r.second)));
|
||||
}
|
||||
}
|
||||
other->m_renderers.clear();
|
||||
}
|
||||
|
||||
void LayerRenderer::SetLayout(TWidgetsLayoutInfo const & info)
|
||||
{
|
||||
for (auto const & node : info)
|
||||
{
|
||||
auto renderer = m_renderers.find(node.first);
|
||||
if (renderer != m_renderers.end())
|
||||
renderer->second->SetPivot(m2::PointF(node.second));
|
||||
}
|
||||
}
|
||||
|
||||
void LayerRenderer::DestroyRenderers()
|
||||
{
|
||||
m_renderers.clear();
|
||||
}
|
||||
|
||||
void LayerRenderer::AddShapeRenderer(EWidget widget, drape_ptr<ShapeRenderer> && shape)
|
||||
{
|
||||
if (shape == nullptr)
|
||||
return;
|
||||
|
||||
VERIFY(m_renderers.insert(std::make_pair(widget, std::move(shape))).second, ());
|
||||
}
|
||||
|
||||
bool LayerRenderer::OnTouchDown(m2::RectD const & touchArea)
|
||||
{
|
||||
for (auto & r : m_renderers)
|
||||
{
|
||||
m_activeOverlay = r.second->ProcessTapEvent(touchArea);
|
||||
if (m_activeOverlay != nullptr)
|
||||
{
|
||||
m_activeOverlayId = m_activeOverlay->GetOverlayID().m_featureId;
|
||||
m_activeOverlay->OnTapBegin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LayerRenderer::OnTouchUp(m2::RectD const & touchArea)
|
||||
{
|
||||
if (m_activeOverlay != nullptr)
|
||||
{
|
||||
if (m_activeOverlay->IsTapped(touchArea))
|
||||
m_activeOverlay->OnTap();
|
||||
|
||||
m_activeOverlay->OnTapEnd();
|
||||
m_activeOverlay = nullptr;
|
||||
m_activeOverlayId = FeatureID();
|
||||
}
|
||||
}
|
||||
|
||||
void LayerRenderer::OnTouchCancel(m2::RectD const & touchArea)
|
||||
{
|
||||
UNUSED_VALUE(touchArea);
|
||||
if (m_activeOverlay != nullptr)
|
||||
{
|
||||
m_activeOverlay->OnTapEnd();
|
||||
m_activeOverlay = nullptr;
|
||||
m_activeOverlayId = FeatureID();
|
||||
}
|
||||
}
|
||||
|
||||
bool LayerRenderer::HasWidget(EWidget widget) const
|
||||
{
|
||||
return m_renderers.find(widget) != m_renderers.end();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class ScaleFpsLabelHandle : public MutableLabelHandle
|
||||
{
|
||||
using TBase = MutableLabelHandle;
|
||||
|
||||
public:
|
||||
ScaleFpsLabelHandle(uint32_t id, ref_ptr<dp::TextureManager> textures, std::string const & apiLabel,
|
||||
Position const & position)
|
||||
: TBase(id, position.m_anchor, position.m_pixelPivot, textures)
|
||||
, m_apiLabel(apiLabel)
|
||||
{
|
||||
SetIsVisible(true);
|
||||
}
|
||||
|
||||
bool Update(ScreenBase const & screen) override
|
||||
{
|
||||
auto & helper = gui::DrapeGui::Instance().GetScaleFpsHelper();
|
||||
if (!helper.IsVisible())
|
||||
return false;
|
||||
|
||||
if (m_scale != helper.GetScale() || m_fps != helper.GetFps() || m_isPaused != helper.IsPaused())
|
||||
{
|
||||
m_scale = helper.GetScale();
|
||||
m_fps = helper.GetFps();
|
||||
m_isPaused = helper.IsPaused();
|
||||
std::stringstream ss;
|
||||
ss << m_apiLabel << ": Scale: " << m_scale << " / FPS: " << m_fps;
|
||||
if (m_isPaused)
|
||||
ss << " (PAUSED)";
|
||||
SetContent(ss.str());
|
||||
}
|
||||
|
||||
return TBase::Update(screen);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string const m_apiLabel;
|
||||
int m_scale = 1;
|
||||
uint32_t m_fps = 0;
|
||||
bool m_isPaused = false;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
drape_ptr<LayerRenderer> LayerCacher::RecacheWidgets(ref_ptr<dp::GraphicsContext> context,
|
||||
TWidgetsInitInfo const & initInfo,
|
||||
ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
using TCacheShape = std::function<void(ref_ptr<dp::GraphicsContext>, Position anchor, ref_ptr<LayerRenderer> renderer,
|
||||
ref_ptr<dp::TextureManager> textures)>;
|
||||
static std::map<EWidget, TCacheShape> cacheFunctions{
|
||||
{WIDGET_COMPASS, std::bind(&LayerCacher::CacheCompass, this, _1, _2, _3, _4)},
|
||||
{WIDGET_RULER, std::bind(&LayerCacher::CacheRuler, this, _1, _2, _3, _4)},
|
||||
{WIDGET_COPYRIGHT, std::bind(&LayerCacher::CacheCopyright, this, _1, _2, _3, _4)},
|
||||
{WIDGET_SCALE_FPS_LABEL, std::bind(&LayerCacher::CacheScaleFpsLabel, this, _1, _2, _3, _4)},
|
||||
};
|
||||
|
||||
drape_ptr<LayerRenderer> renderer = make_unique_dp<LayerRenderer>();
|
||||
for (auto const & node : initInfo)
|
||||
{
|
||||
auto cacheFunction = cacheFunctions.find(node.first);
|
||||
if (cacheFunction != cacheFunctions.end())
|
||||
cacheFunction->second(context, node.second, make_ref(renderer), textures);
|
||||
}
|
||||
|
||||
// Flush gui geometry.
|
||||
context->Flush();
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
drape_ptr<LayerRenderer> LayerCacher::RecacheChoosePositionMark(ref_ptr<dp::GraphicsContext> context,
|
||||
ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
m2::PointF const surfSize = DrapeGui::Instance().GetSurfaceSize();
|
||||
drape_ptr<LayerRenderer> renderer = make_unique_dp<LayerRenderer>();
|
||||
|
||||
ChoosePositionMark positionMark = ChoosePositionMark(Position(surfSize * 0.5f, dp::Center));
|
||||
renderer->AddShapeRenderer(WIDGET_CHOOSE_POSITION_MARK, positionMark.Draw(context, textures));
|
||||
|
||||
// Flush gui geometry.
|
||||
context->Flush();
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
#ifdef RENDER_DEBUG_INFO_LABELS
|
||||
drape_ptr<LayerRenderer> LayerCacher::RecacheDebugLabels(ref_ptr<dp::GraphicsContext> context,
|
||||
ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
drape_ptr<LayerRenderer> renderer = make_unique_dp<LayerRenderer>();
|
||||
|
||||
auto const vs = static_cast<float>(df::VisualParams::Instance().GetVisualScale());
|
||||
DebugInfoLabels debugLabels = DebugInfoLabels(Position(m2::PointF(10.0f * vs, 50.0f * vs), dp::Center));
|
||||
|
||||
debugLabels.AddLabel(textures,
|
||||
"visible: km2, readed: km2, ratio:", [](ScreenBase const & screen, string & content) -> bool
|
||||
{
|
||||
double const sizeX = screen.PixelRectIn3d().SizeX();
|
||||
double const sizeY = screen.PixelRectIn3d().SizeY();
|
||||
|
||||
m2::PointD const p0 = screen.PtoG(screen.P3dtoP(m2::PointD(0.0, 0.0)));
|
||||
m2::PointD const p1 = screen.PtoG(screen.P3dtoP(m2::PointD(0.0, sizeY)));
|
||||
m2::PointD const p2 = screen.PtoG(screen.P3dtoP(m2::PointD(sizeX, sizeY)));
|
||||
m2::PointD const p3 = screen.PtoG(screen.P3dtoP(m2::PointD(sizeX, 0.0)));
|
||||
|
||||
double const areaG = mercator::AreaOnEarth(p0, p1, p2) + mercator::AreaOnEarth(p2, p3, p0);
|
||||
|
||||
double const sizeX_2d = screen.PixelRect().SizeX();
|
||||
double const sizeY_2d = screen.PixelRect().SizeY();
|
||||
|
||||
m2::PointD const p0_2d = screen.PtoG(m2::PointD(0.0, 0.0));
|
||||
m2::PointD const p1_2d = screen.PtoG(m2::PointD(0.0, sizeY_2d));
|
||||
m2::PointD const p2_2d = screen.PtoG(m2::PointD(sizeX_2d, sizeY_2d));
|
||||
m2::PointD const p3_2d = screen.PtoG(m2::PointD(sizeX_2d, 0.0));
|
||||
|
||||
double const areaGTotal = mercator::AreaOnEarth(p0_2d, p1_2d, p2_2d) + mercator::AreaOnEarth(p2_2d, p3_2d, p0_2d);
|
||||
|
||||
std::ostringstream out;
|
||||
out << std::fixed << std::setprecision(2) << "visible: " << areaG / 1000000.0 << " km2"
|
||||
<< ", readed: " << areaGTotal / 1000000.0 << " km2"
|
||||
<< ", ratio: " << areaGTotal / areaG;
|
||||
content.assign(out.str());
|
||||
return true;
|
||||
});
|
||||
|
||||
debugLabels.AddLabel(textures, "scale2d: m/px, scale2d * vs: m/px",
|
||||
[](ScreenBase const & screen, string & content) -> bool
|
||||
{
|
||||
double const distanceG = mercator::DistanceOnEarth(screen.PtoG(screen.PixelRect().LeftBottom()),
|
||||
screen.PtoG(screen.PixelRect().RightBottom()));
|
||||
|
||||
double const vs = df::VisualParams::Instance().GetVisualScale();
|
||||
double const scale = distanceG / screen.PixelRect().SizeX();
|
||||
|
||||
std::ostringstream out;
|
||||
out << std::fixed << std::setprecision(2) << "scale2d: " << scale << " m/px"
|
||||
<< ", scale2d * vs: " << scale * vs << " m/px";
|
||||
content.assign(out.str());
|
||||
return true;
|
||||
});
|
||||
|
||||
debugLabels.AddLabel(textures, "distance: m", [](ScreenBase const & screen, string & content) -> bool
|
||||
{
|
||||
double const sizeX = screen.PixelRectIn3d().SizeX();
|
||||
double const sizeY = screen.PixelRectIn3d().SizeY();
|
||||
|
||||
double const distance = mercator::DistanceOnEarth(screen.PtoG(screen.P3dtoP(m2::PointD(sizeX / 2.0, 0.0))),
|
||||
screen.PtoG(screen.P3dtoP(m2::PointD(sizeX / 2.0, sizeY))));
|
||||
|
||||
std::ostringstream out;
|
||||
out << std::fixed << std::setprecision(2) << "distance: " << distance << " m";
|
||||
content.assign(out.str());
|
||||
return true;
|
||||
});
|
||||
|
||||
debugLabels.AddLabel(textures, "angle: ", [](ScreenBase const & screen, string & content) -> bool
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << std::fixed << std::setprecision(2) << "angle: " << screen.GetRotationAngle() * 180.0 / math::pi;
|
||||
content.assign(out.str());
|
||||
return true;
|
||||
});
|
||||
|
||||
renderer->AddShapeRenderer(WIDGET_DEBUG_INFO, debugLabels.Draw(context, textures));
|
||||
|
||||
// Flush gui geometry.
|
||||
context->Flush();
|
||||
|
||||
return renderer;
|
||||
}
|
||||
#endif
|
||||
|
||||
void LayerCacher::CacheCompass(ref_ptr<dp::GraphicsContext> context, Position const & position,
|
||||
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
Compass compass = Compass(position);
|
||||
drape_ptr<ShapeRenderer> shape =
|
||||
compass.Draw(context, textures, std::bind(&DrapeGui::CallOnCompassTappedHandler, &DrapeGui::Instance()));
|
||||
|
||||
renderer->AddShapeRenderer(WIDGET_COMPASS, std::move(shape));
|
||||
}
|
||||
|
||||
void LayerCacher::CacheRuler(ref_ptr<dp::GraphicsContext> context, Position const & position,
|
||||
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
renderer->AddShapeRenderer(WIDGET_RULER, Ruler(position).Draw(context, textures));
|
||||
}
|
||||
|
||||
void LayerCacher::CacheCopyright(ref_ptr<dp::GraphicsContext> context, Position const & position,
|
||||
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
renderer->AddShapeRenderer(WIDGET_COPYRIGHT, CopyrightLabel(position).Draw(context, textures));
|
||||
}
|
||||
|
||||
void LayerCacher::CacheScaleFpsLabel(ref_ptr<dp::GraphicsContext> context, Position const & position,
|
||||
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
|
||||
{
|
||||
MutableLabelDrawer::Params params;
|
||||
params.m_alphabet = "MGLFPSAUEDVcale: 1234567890/()";
|
||||
params.m_maxLength = 50;
|
||||
params.m_anchor = position.m_anchor;
|
||||
params.m_font = DrapeGui::GetGuiTextFont();
|
||||
params.m_pivot = position.m_pixelPivot;
|
||||
auto const apiVersion = context->GetApiVersion();
|
||||
params.m_handleCreator = [textures, apiVersion, &position](dp::Anchor, m2::PointF const &)
|
||||
{
|
||||
std::string apiLabel;
|
||||
switch (apiVersion)
|
||||
{
|
||||
case dp::ApiVersion::OpenGLES3: apiLabel = "GL3"; break;
|
||||
case dp::ApiVersion::Metal: apiLabel = "M"; break;
|
||||
case dp::ApiVersion::Vulkan: apiLabel = "V"; break;
|
||||
case dp::ApiVersion::Invalid: CHECK(false, ("Invalid API version.")); break;
|
||||
}
|
||||
return make_unique_dp<ScaleFpsLabelHandle>(EGuiHandle::GuiHandleScaleLabel, textures, apiLabel, position);
|
||||
};
|
||||
|
||||
drape_ptr<ShapeRenderer> scaleRenderer = make_unique_dp<ShapeRenderer>();
|
||||
MutableLabelDrawer::Draw(context, params, textures, std::bind(&ShapeRenderer::AddShape, scaleRenderer.get(), _1, _2));
|
||||
|
||||
renderer->AddShapeRenderer(WIDGET_SCALE_FPS_LABEL, std::move(scaleRenderer));
|
||||
}
|
||||
} // namespace gui
|
||||
82
libs/drape_frontend/gui/layer_render.hpp
Normal file
82
libs/drape_frontend/gui/layer_render.hpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/shape.hpp"
|
||||
#include "drape_frontend/gui/skin.hpp"
|
||||
|
||||
#include "shaders/program_manager.hpp"
|
||||
|
||||
#include "drape/texture_manager.hpp"
|
||||
|
||||
#include "geometry/screenbase.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GraphicsContext;
|
||||
} // namespace dp
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class LayerRenderer
|
||||
{
|
||||
public:
|
||||
LayerRenderer() = default;
|
||||
~LayerRenderer();
|
||||
|
||||
void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng);
|
||||
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, bool routingActive,
|
||||
ScreenBase const & screen);
|
||||
void Merge(ref_ptr<LayerRenderer> other);
|
||||
void SetLayout(gui::TWidgetsLayoutInfo const & info);
|
||||
|
||||
bool OnTouchDown(m2::RectD const & touchArea);
|
||||
void OnTouchUp(m2::RectD const & touchArea);
|
||||
void OnTouchCancel(m2::RectD const & touchArea);
|
||||
|
||||
bool HasWidget(EWidget widget) const;
|
||||
|
||||
private:
|
||||
void DestroyRenderers();
|
||||
|
||||
friend class LayerCacher;
|
||||
void AddShapeRenderer(EWidget widget, drape_ptr<ShapeRenderer> && shape);
|
||||
|
||||
private:
|
||||
using TRenderers = std::map<EWidget, drape_ptr<ShapeRenderer>>;
|
||||
TRenderers m_renderers;
|
||||
|
||||
ref_ptr<gui::Handle> m_activeOverlay;
|
||||
FeatureID m_activeOverlayId;
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(LayerRenderer);
|
||||
};
|
||||
|
||||
class LayerCacher
|
||||
{
|
||||
public:
|
||||
drape_ptr<LayerRenderer> RecacheWidgets(ref_ptr<dp::GraphicsContext> context, TWidgetsInitInfo const & initInfo,
|
||||
ref_ptr<dp::TextureManager> textures);
|
||||
drape_ptr<LayerRenderer> RecacheChoosePositionMark(ref_ptr<dp::GraphicsContext> context,
|
||||
ref_ptr<dp::TextureManager> textures);
|
||||
|
||||
#ifdef RENDER_DEBUG_INFO_LABELS
|
||||
drape_ptr<LayerRenderer> RecacheDebugLabels(ref_ptr<dp::GraphicsContext> context,
|
||||
ref_ptr<dp::TextureManager> textures);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void CacheCompass(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
|
||||
ref_ptr<dp::TextureManager> textures);
|
||||
void CacheRuler(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
|
||||
ref_ptr<dp::TextureManager> textures);
|
||||
void CacheCopyright(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
|
||||
ref_ptr<dp::TextureManager> textures);
|
||||
void CacheScaleFpsLabel(ref_ptr<dp::GraphicsContext> context, Position const & position,
|
||||
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures);
|
||||
void CacheWatermark(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
|
||||
ref_ptr<dp::TextureManager> textures);
|
||||
};
|
||||
} // namespace gui
|
||||
243
libs/drape_frontend/gui/ruler.cpp
Normal file
243
libs/drape_frontend/gui/ruler.cpp
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
#include "drape_frontend/gui/ruler.hpp"
|
||||
|
||||
#include "drape_frontend/animation/show_hide_animation.hpp"
|
||||
#include "drape_frontend/batcher_bucket.hpp"
|
||||
#include "drape_frontend/gui/drape_gui.hpp"
|
||||
#include "drape_frontend/gui/gui_text.hpp"
|
||||
#include "drape_frontend/gui/ruler_helper.hpp"
|
||||
|
||||
#include "shaders/programs.hpp"
|
||||
|
||||
#include "drape/glsl_types.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct RulerVertex
|
||||
{
|
||||
RulerVertex() = default;
|
||||
RulerVertex(glsl::vec2 const & pos, glsl::vec2 const & normal, glsl::vec2 const & texCoord)
|
||||
: m_position(pos)
|
||||
, m_normal(normal)
|
||||
, m_texCoord(texCoord)
|
||||
{}
|
||||
|
||||
glsl::vec2 m_position;
|
||||
glsl::vec2 m_normal;
|
||||
glsl::vec2 m_texCoord;
|
||||
};
|
||||
|
||||
dp::BindingInfo GetBindingInfo()
|
||||
{
|
||||
dp::BindingInfo info(3);
|
||||
uint8_t offset = 0;
|
||||
offset += dp::FillDecl<glsl::vec2, RulerVertex>(0, "a_position", info, offset);
|
||||
offset += dp::FillDecl<glsl::vec2, RulerVertex>(1, "a_normal", info, offset);
|
||||
/*offset += */ dp::FillDecl<glsl::vec2, RulerVertex>(2, "a_colorTexCoords", info, offset);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
template <typename TBase>
|
||||
class BaseRulerHandle : public TBase
|
||||
{
|
||||
public:
|
||||
BaseRulerHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, bool isAppearing)
|
||||
: TBase(id, anchor, pivot)
|
||||
, m_isAppearing(isAppearing)
|
||||
, m_isVisibleAtEnd(true)
|
||||
, m_animation(false, 0.4)
|
||||
{}
|
||||
|
||||
bool Update(ScreenBase const & screen)
|
||||
{
|
||||
RulerHelper & helper = DrapeGui::GetRulerHelper();
|
||||
|
||||
TBase::SetIsVisible(true);
|
||||
bool isVisible = RulerHelper::IsVisible(screen);
|
||||
if (!isVisible)
|
||||
{
|
||||
m_animation.HideAnimated();
|
||||
m_isVisibleAtEnd = false;
|
||||
}
|
||||
else if (helper.IsTextDirty())
|
||||
{
|
||||
m_isAppearing = !m_isAppearing;
|
||||
if (m_isAppearing)
|
||||
{
|
||||
m_animation.ShowAnimated();
|
||||
m_isVisibleAtEnd = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_animation.HideAnimated();
|
||||
m_isVisibleAtEnd = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool const result = TBase::Update(screen);
|
||||
UpdateImpl(screen, helper);
|
||||
|
||||
if (m_animation.IsFinished())
|
||||
TBase::SetIsVisible(m_isVisibleAtEnd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void UpdateImpl(ScreenBase const & /*screen*/, RulerHelper const & /*helper*/) {}
|
||||
|
||||
bool IsAppearing() const { return m_isAppearing; }
|
||||
float GetOpacity() const { return static_cast<float>(m_animation.GetT()); }
|
||||
|
||||
private:
|
||||
bool m_isAppearing;
|
||||
bool m_isVisibleAtEnd;
|
||||
df::ShowHideAnimation m_animation;
|
||||
};
|
||||
|
||||
class RulerHandle : public BaseRulerHandle<Handle>
|
||||
{
|
||||
using TBase = BaseRulerHandle<Handle>;
|
||||
|
||||
public:
|
||||
RulerHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, bool appearing)
|
||||
: TBase(id, anchor, pivot, appearing)
|
||||
{}
|
||||
|
||||
private:
|
||||
void UpdateImpl(ScreenBase const & screen, RulerHelper const & helper) override
|
||||
{
|
||||
if (!IsVisible())
|
||||
return;
|
||||
|
||||
if (IsAppearing())
|
||||
m_params.m_length = helper.GetRulerPixelLength();
|
||||
m_params.m_position = m_pivot;
|
||||
m_params.m_opacity = GetOpacity();
|
||||
}
|
||||
};
|
||||
|
||||
class RulerTextHandle : public BaseRulerHandle<MutableLabelHandle>
|
||||
{
|
||||
using TBase = BaseRulerHandle<MutableLabelHandle>;
|
||||
|
||||
public:
|
||||
RulerTextHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, bool isAppearing,
|
||||
ref_ptr<dp::TextureManager> textures)
|
||||
: TBase(id, anchor, pivot, isAppearing)
|
||||
, m_firstUpdate(true)
|
||||
{
|
||||
SetTextureManager(textures);
|
||||
}
|
||||
|
||||
bool Update(ScreenBase const & screen) override
|
||||
{
|
||||
SetIsVisible(RulerHelper::IsVisible(screen));
|
||||
if (IsVisible() && (DrapeGui::GetRulerHelper().IsTextDirty() || m_firstUpdate))
|
||||
{
|
||||
SetContent(DrapeGui::GetRulerHelper().GetRulerText());
|
||||
m_firstUpdate = false;
|
||||
}
|
||||
|
||||
return TBase::Update(screen);
|
||||
}
|
||||
|
||||
void SetPivot(glsl::vec2 const & pivot) override
|
||||
{
|
||||
TBase::SetPivot(pivot + glsl::vec2(0.0, RulerHelper::GetVerticalTextOffset() - RulerHelper::GetRulerHalfHeight()));
|
||||
}
|
||||
|
||||
protected:
|
||||
void UpdateImpl(ScreenBase const & /*screen*/, RulerHelper const & /*helper*/) override
|
||||
{
|
||||
if (IsVisible())
|
||||
m_params.m_opacity = GetOpacity();
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_firstUpdate;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
drape_ptr<ShapeRenderer> Ruler::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const
|
||||
{
|
||||
ShapeControl control;
|
||||
DrawRuler(context, control, tex, true);
|
||||
DrawRuler(context, control, tex, false);
|
||||
DrawText(context, control, tex, true);
|
||||
DrawText(context, control, tex, false);
|
||||
|
||||
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
|
||||
renderer->AddShapeControl(std::move(control));
|
||||
return renderer;
|
||||
}
|
||||
|
||||
void Ruler::DrawRuler(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
|
||||
bool isAppearing) const
|
||||
{
|
||||
buffer_vector<RulerVertex, 4> data;
|
||||
|
||||
dp::TextureManager::ColorRegion reg;
|
||||
tex->GetColorRegion(DrapeGui::GetGuiTextFont().m_color, reg);
|
||||
|
||||
glsl::vec2 texCoord = glsl::ToVec2(reg.GetTexRect().Center());
|
||||
float const h = RulerHelper::GetRulerHalfHeight();
|
||||
|
||||
glsl::vec2 normals[] = {
|
||||
glsl::vec2(-1.0, 0.0),
|
||||
glsl::vec2(1.0, 0.0),
|
||||
};
|
||||
|
||||
dp::Anchor anchor = m_position.m_anchor;
|
||||
if (anchor & dp::Left)
|
||||
normals[0] = glsl::vec2(0.0, 0.0);
|
||||
else if (anchor & dp::Right)
|
||||
normals[1] = glsl::vec2(0.0, 0.0);
|
||||
|
||||
data.push_back(RulerVertex(glsl::vec2(0.0, h), normals[0], texCoord));
|
||||
data.push_back(RulerVertex(glsl::vec2(0.0, -h), normals[0], texCoord));
|
||||
data.push_back(RulerVertex(glsl::vec2(0.0, h), normals[1], texCoord));
|
||||
data.push_back(RulerVertex(glsl::vec2(0.0, -h), normals[1], texCoord));
|
||||
|
||||
auto state = df::CreateRenderState(gpu::Program::Ruler, df::DepthLayer::GuiLayer);
|
||||
state.SetColorTexture(reg.GetTexture());
|
||||
state.SetDepthTestEnabled(false);
|
||||
|
||||
dp::AttributeProvider provider(1, 4);
|
||||
provider.InitStream(0, GetBindingInfo(), make_ref(data.data()));
|
||||
|
||||
{
|
||||
dp::Batcher batcher(dp::Batcher::IndexPerQuad, dp::Batcher::VertexPerQuad);
|
||||
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
|
||||
dp::SessionGuard guard(context, batcher, std::bind(&ShapeControl::AddShape, &control, _1, _2));
|
||||
batcher.InsertTriangleStrip(context, state, make_ref(&provider),
|
||||
make_unique_dp<RulerHandle>(EGuiHandle::GuiHandleRuler, m_position.m_anchor,
|
||||
m_position.m_pixelPivot, isAppearing));
|
||||
}
|
||||
}
|
||||
|
||||
void Ruler::DrawText(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
|
||||
bool isAppearing) const
|
||||
{
|
||||
std::string alphabet;
|
||||
uint32_t maxTextLength;
|
||||
RulerHelper::GetTextInitInfo(alphabet, maxTextLength);
|
||||
|
||||
MutableLabelDrawer::Params params;
|
||||
params.m_anchor = static_cast<dp::Anchor>((m_position.m_anchor & (dp::Right | dp::Left)) | dp::Bottom);
|
||||
params.m_alphabet = alphabet;
|
||||
params.m_maxLength = maxTextLength;
|
||||
params.m_font = DrapeGui::GetGuiTextFont();
|
||||
params.m_pivot = m_position.m_pixelPivot + m2::PointF(0.0f, RulerHelper::GetVerticalTextOffset());
|
||||
params.m_handleCreator = [isAppearing, tex](dp::Anchor anchor, m2::PointF const & pivot)
|
||||
{ return make_unique_dp<RulerTextHandle>(EGuiHandle::GuiHandleRulerLabel, anchor, pivot, isAppearing, tex); };
|
||||
|
||||
MutableLabelDrawer::Draw(context, params, tex, std::bind(&ShapeControl::AddShape, &control, _1, _2));
|
||||
}
|
||||
} // namespace gui
|
||||
19
libs/drape_frontend/gui/ruler.hpp
Normal file
19
libs/drape_frontend/gui/ruler.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/shape.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class Ruler : public Shape
|
||||
{
|
||||
public:
|
||||
explicit Ruler(Position const & position) : Shape(position) {}
|
||||
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const;
|
||||
|
||||
private:
|
||||
void DrawRuler(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
|
||||
bool isAppearing) const;
|
||||
void DrawText(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
|
||||
bool isAppearing) const;
|
||||
};
|
||||
} // namespace gui
|
||||
237
libs/drape_frontend/gui/ruler_helper.cpp
Normal file
237
libs/drape_frontend/gui/ruler_helper.cpp
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
#include "drape_frontend/gui/ruler_helper.hpp"
|
||||
#include "drape_frontend/gui/drape_gui.hpp"
|
||||
#include "drape_frontend/visual_params.hpp"
|
||||
|
||||
#include "platform/measurement_utils.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
#include "geometry/screenbase.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
namespace
|
||||
{
|
||||
float constexpr kMinPixelWidth = 60.f;
|
||||
int constexpr kMinMetersWidth = 5;
|
||||
int constexpr kMaxMetersWidth = 1000000;
|
||||
|
||||
int constexpr kMinUnitValue = -1;
|
||||
int constexpr kMaxUnitValue = std::numeric_limits<int>::max() - 1;
|
||||
int constexpr kInvalidUnitValue = kMaxUnitValue + 1;
|
||||
|
||||
int constexpr kVisibleRulerBottomScale = 5;
|
||||
|
||||
struct UnitValue
|
||||
{
|
||||
char const * m_s;
|
||||
int m_i;
|
||||
};
|
||||
|
||||
// TODO: Localize these strings.
|
||||
UnitValue constexpr g_arrFeets[] = {
|
||||
{"10 ft", 10}, {"20 ft", 20}, {"50 ft", 50}, {"100 ft", 100}, {"200 ft", 200},
|
||||
{"0.1 mi", 528}, {"0.2 mi", 528 * 2}, {"0.5 mi", 528 * 5}, {"1 mi", 5280}, {"2 mi", 2 * 5280},
|
||||
{"5 mi", 5 * 5280}, {"10 mi", 10 * 5280}, {"20 mi", 20 * 5280}, {"50 mi", 50 * 5280}, {"100 mi", 100 * 5280},
|
||||
{"200 mi", 200 * 5280}, {"500 mi", 500 * 5280}};
|
||||
|
||||
UnitValue constexpr g_arrYards[] = {
|
||||
{"50 yd", 50}, {"100 yd", 100}, {"200 yd", 200}, {"500 yd", 500}, {"0.5 mi", 1760 / 2},
|
||||
{"1 mi", 1760}, {"2 mi", 2 * 1760}, {"5 mi", 5 * 1760}, {"10 mi", 10 * 1760}, {"20 mi", 20 * 1760},
|
||||
{"50 mi", 50 * 1760}, {"100 mi", 100 * 1760}, {"200 mi", 200 * 1760}, {"500 mi", 500 * 1760}};
|
||||
|
||||
// TODO: fix ruler text to the current zoom level, i.e. make it 100m for z16 always
|
||||
// (ruler length will vary still). It'll make debugging and user support easier as
|
||||
// we'll be able to tell zoom level of a screenshot by looking at the ruler.
|
||||
UnitValue constexpr g_arrMeters[] = {{"1 m", 1}, {"2 m", 2}, {"5 m", 5}, {"10 m", 10},
|
||||
{"20 m", 20}, {"50 m", 50}, {"100 m", 100}, {"200 m", 200},
|
||||
{"500 m", 500}, {"1 km", 1000}, {"2 km", 2000}, {"5 km", 5000},
|
||||
{"10 km", 10000}, {"20 km", 20000}, {"50 km", 50000}, {"100 km", 100000},
|
||||
{"200 km", 200000}, {"500 km", 500000}, {"1000 km", 1000000}};
|
||||
|
||||
double Identity(double val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
RulerHelper::RulerHelper()
|
||||
: m_pixelLength(0.0f)
|
||||
, m_rangeIndex(kInvalidUnitValue)
|
||||
, m_isTextDirty(false)
|
||||
, m_dirtyTextRequested(false)
|
||||
{}
|
||||
|
||||
void RulerHelper::Update(ScreenBase const & screen)
|
||||
{
|
||||
m2::PointD pivot = screen.PixelRect().Center();
|
||||
long const minPxWidth = std::lround(kMinPixelWidth * df::VisualParams::Instance().GetVisualScale());
|
||||
m2::PointD pt1 = screen.PtoG(pivot);
|
||||
m2::PointD pt0 = screen.PtoG(pivot - m2::PointD(minPxWidth, 0));
|
||||
|
||||
double const distanceInMeters = mercator::DistanceOnEarth(pt0, pt1);
|
||||
|
||||
// convert metres to units for calculating m_metresDiff.
|
||||
double metersDiff = CalcMetersDiff(distanceInMeters);
|
||||
|
||||
bool const higherThanMax = metersDiff > kMaxMetersWidth;
|
||||
ASSERT_GREATER_OR_EQUAL(metersDiff, kMinMetersWidth, ());
|
||||
m_pixelLength = static_cast<float>(minPxWidth);
|
||||
|
||||
if (higherThanMax)
|
||||
m_pixelLength = static_cast<float>(minPxWidth) * 3.0f / 2.0f;
|
||||
else
|
||||
{
|
||||
double const a = ang::AngleTo(pt1, pt0);
|
||||
pt0 = mercator::GetSmPoint(pt1, cos(a) * metersDiff, sin(a) * metersDiff);
|
||||
|
||||
m_pixelLength = std::round(pivot.Length(screen.GtoP(pt0)));
|
||||
}
|
||||
|
||||
int drawScale = df::GetDrawTileScale(screen);
|
||||
if (m_currentDrawScale < kVisibleRulerBottomScale && drawScale >= kVisibleRulerBottomScale)
|
||||
SetTextDirty();
|
||||
|
||||
m_currentDrawScale = drawScale;
|
||||
}
|
||||
|
||||
// static
|
||||
bool RulerHelper::IsVisible(ScreenBase const & screen)
|
||||
{
|
||||
DrapeGui & gui = DrapeGui::Instance();
|
||||
return !gui.IsCopyrightActive() && df::GetDrawTileScale(screen) >= kVisibleRulerBottomScale;
|
||||
}
|
||||
|
||||
void RulerHelper::Invalidate()
|
||||
{
|
||||
SetTextDirty();
|
||||
}
|
||||
|
||||
// static
|
||||
float RulerHelper::GetRulerHalfHeight()
|
||||
{
|
||||
float constexpr kRulerHalfHeight = 1.0f;
|
||||
return kRulerHalfHeight * static_cast<float>(df::VisualParams::Instance().GetVisualScale());
|
||||
}
|
||||
|
||||
float RulerHelper::GetRulerPixelLength() const
|
||||
{
|
||||
return m_pixelLength;
|
||||
}
|
||||
|
||||
// static
|
||||
float RulerHelper::GetMaxRulerPixelLength()
|
||||
{
|
||||
return kMinPixelWidth * 3.0f / 2.0f;
|
||||
}
|
||||
|
||||
// static
|
||||
int RulerHelper::GetVerticalTextOffset()
|
||||
{
|
||||
return static_cast<int>(-5 * df::VisualParams::Instance().GetVisualScale());
|
||||
}
|
||||
|
||||
bool RulerHelper::IsTextDirty() const
|
||||
{
|
||||
return m_isTextDirty;
|
||||
}
|
||||
|
||||
std::string const & RulerHelper::GetRulerText() const
|
||||
{
|
||||
m_dirtyTextRequested = true;
|
||||
return m_rulerText;
|
||||
}
|
||||
|
||||
void RulerHelper::ResetTextDirtyFlag()
|
||||
{
|
||||
if (m_dirtyTextRequested)
|
||||
m_isTextDirty = false;
|
||||
}
|
||||
|
||||
// static
|
||||
void RulerHelper::GetTextInitInfo(std::string & alphabet, uint32_t & size)
|
||||
{
|
||||
std::set<char> symbols;
|
||||
size_t result = 0;
|
||||
auto const functor = [&result, &symbols](UnitValue const & v)
|
||||
{
|
||||
size_t stringSize = strlen(v.m_s);
|
||||
result = std::max(result, stringSize);
|
||||
for (size_t i = 0; i < stringSize; ++i)
|
||||
symbols.insert(v.m_s[i]);
|
||||
};
|
||||
|
||||
std::for_each(std::begin(g_arrFeets), std::end(g_arrFeets), functor);
|
||||
std::for_each(std::begin(g_arrMeters), std::end(g_arrMeters), functor);
|
||||
std::for_each(std::begin(g_arrYards), std::end(g_arrYards), functor);
|
||||
|
||||
std::for_each(begin(symbols), end(symbols), [&alphabet](char c) { alphabet.push_back(c); });
|
||||
alphabet.append("<>");
|
||||
|
||||
size = static_cast<uint32_t>(result) + 2; // add 2 char for symbols "< " and "> ".
|
||||
}
|
||||
|
||||
double RulerHelper::CalcMetersDiff(double value)
|
||||
{
|
||||
UnitValue const * arrU = g_arrMeters;
|
||||
int count = ARRAY_SIZE(g_arrMeters);
|
||||
|
||||
auto conversionFn = &Identity;
|
||||
|
||||
using namespace measurement_utils;
|
||||
if (GetMeasurementUnits() == Units::Imperial)
|
||||
{
|
||||
arrU = g_arrFeets;
|
||||
count = ARRAY_SIZE(g_arrFeets);
|
||||
conversionFn = &MetersToFeet;
|
||||
}
|
||||
|
||||
int prevUnitRange = m_rangeIndex;
|
||||
double result = 0.0;
|
||||
double v = conversionFn(value);
|
||||
if (arrU[0].m_i > v)
|
||||
{
|
||||
m_rangeIndex = kMinUnitValue;
|
||||
// TODO: "< X" ruler text seems to be never used.
|
||||
m_rulerText = std::string("< ") + arrU[0].m_s;
|
||||
result = kMinMetersWidth - 1.0;
|
||||
}
|
||||
else if (arrU[count - 1].m_i <= v)
|
||||
{
|
||||
m_rangeIndex = kMaxUnitValue;
|
||||
m_rulerText = std::string("> ") + arrU[count - 1].m_s;
|
||||
result = kMaxMetersWidth + 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
if (arrU[i].m_i > v)
|
||||
{
|
||||
m_rangeIndex = i;
|
||||
result = arrU[i].m_i / conversionFn(1.0);
|
||||
m_rulerText = arrU[i].m_s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_rangeIndex != prevUnitRange)
|
||||
SetTextDirty();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void RulerHelper::SetTextDirty()
|
||||
{
|
||||
m_dirtyTextRequested = false;
|
||||
m_isTextDirty = true;
|
||||
}
|
||||
} // namespace gui
|
||||
40
libs/drape_frontend/gui/ruler_helper.hpp
Normal file
40
libs/drape_frontend/gui/ruler_helper.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class ScreenBase;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class RulerHelper
|
||||
{
|
||||
public:
|
||||
RulerHelper();
|
||||
|
||||
void Update(ScreenBase const & screen);
|
||||
static bool IsVisible(ScreenBase const & screen);
|
||||
void Invalidate();
|
||||
|
||||
static float GetRulerHalfHeight();
|
||||
float GetRulerPixelLength() const;
|
||||
static float GetMaxRulerPixelLength();
|
||||
static int GetVerticalTextOffset();
|
||||
bool IsTextDirty() const;
|
||||
std::string const & GetRulerText() const;
|
||||
void ResetTextDirtyFlag();
|
||||
static void GetTextInitInfo(std::string & alphabet, uint32_t & size);
|
||||
|
||||
private:
|
||||
double CalcMetersDiff(double value);
|
||||
void SetTextDirty();
|
||||
|
||||
private:
|
||||
float m_pixelLength;
|
||||
int m_rangeIndex;
|
||||
std::string m_rulerText;
|
||||
bool m_isTextDirty;
|
||||
mutable bool m_dirtyTextRequested;
|
||||
int m_currentDrawScale = 0;
|
||||
};
|
||||
} // namespace gui
|
||||
53
libs/drape_frontend/gui/scale_fps_helper.hpp
Normal file
53
libs/drape_frontend/gui/scale_fps_helper.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class ScaleFpsHelper
|
||||
{
|
||||
public:
|
||||
uint32_t GetFps() const
|
||||
{
|
||||
if (m_frameTime == 0.0)
|
||||
return 0;
|
||||
return static_cast<uint32_t>(1.0 / m_frameTime);
|
||||
}
|
||||
|
||||
bool IsPaused() const { return m_isPaused; }
|
||||
|
||||
void SetFrameTime(double frameTime, bool isActiveFrame)
|
||||
{
|
||||
m_isPaused = !isActiveFrame;
|
||||
m_framesCounter++;
|
||||
m_aggregatedFrameTime += frameTime;
|
||||
|
||||
double constexpr kAggregationPeriod = 0.5;
|
||||
if (m_isPaused || m_fpsAggregationTimer.ElapsedSeconds() >= kAggregationPeriod)
|
||||
{
|
||||
m_frameTime = m_aggregatedFrameTime / m_framesCounter;
|
||||
m_aggregatedFrameTime = 0.0;
|
||||
m_framesCounter = 0;
|
||||
m_fpsAggregationTimer.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
int GetScale() const { return m_scale; }
|
||||
void SetScale(int scale) { m_scale = scale; }
|
||||
|
||||
void SetVisible(bool isVisible) { m_isVisible = isVisible; }
|
||||
bool IsVisible() const { return m_isVisible; }
|
||||
|
||||
private:
|
||||
double m_frameTime = 0.0;
|
||||
int m_scale = 1;
|
||||
bool m_isVisible = false;
|
||||
|
||||
base::Timer m_fpsAggregationTimer;
|
||||
double m_aggregatedFrameTime = 0.0;
|
||||
uint32_t m_framesCounter = 1;
|
||||
bool m_isPaused = false;
|
||||
};
|
||||
} // namespace gui
|
||||
199
libs/drape_frontend/gui/shape.cpp
Normal file
199
libs/drape_frontend/gui/shape.cpp
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
#include "drape_frontend/gui/shape.hpp"
|
||||
|
||||
#include "drape_frontend/visual_params.hpp"
|
||||
|
||||
#include "drape/glsl_func.hpp"
|
||||
#include "drape/utils/projection.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
Handle::Handle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot)
|
||||
: dp::OverlayHandle(dp::OverlayID(FeatureID(MwmSet::MwmId{}, id)), anchor, 0 /* priority */, 1 /* minVisibleScale */,
|
||||
false /* isBillboard */)
|
||||
, m_pivot(glsl::ToVec2(pivot))
|
||||
{}
|
||||
|
||||
bool Handle::Update(ScreenBase const & screen)
|
||||
{
|
||||
using namespace glsl;
|
||||
|
||||
if (IsVisible())
|
||||
{
|
||||
m_params.m_modelView = transpose(translate(mat4(), vec3(m_pivot, 0.0)));
|
||||
auto const & params = df::VisualParams::Instance().GetGlyphVisualParams();
|
||||
m_params.m_contrastGamma = glsl::vec2(params.m_guiContrast, params.m_guiGamma);
|
||||
m_params.m_isOutlinePass = 0.0f;
|
||||
m_params.m_opacity = 1.0f;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Handle::IndexesRequired() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m2::RectD Handle::GetPixelRect(ScreenBase const & screen, bool perspective) const
|
||||
{
|
||||
// There is no need to check intersection of gui elements.
|
||||
UNUSED_VALUE(perspective);
|
||||
return {};
|
||||
}
|
||||
|
||||
void Handle::GetPixelShape(ScreenBase const & screen, bool perspective, dp::OverlayHandle::Rects & rects) const
|
||||
{
|
||||
// There is no need to check intersection of gui elements.
|
||||
UNUSED_VALUE(screen);
|
||||
UNUSED_VALUE(rects);
|
||||
UNUSED_VALUE(perspective);
|
||||
}
|
||||
|
||||
bool TappableHandle::IsTapped(m2::RectD const & touchArea) const
|
||||
{
|
||||
if (!IsVisible())
|
||||
return false;
|
||||
|
||||
if (m_anchor == dp::Anchor::Center)
|
||||
{
|
||||
m2::RectD rect(m_pivot.x - m_size.x * 0.5, m_pivot.y - m_size.y * 0.5, m_pivot.x + m_size.x * 0.5,
|
||||
m_pivot.y + m_size.y * 0.5);
|
||||
return rect.Intersect(touchArea);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LWARNING, ("Tapping on an overlay is not supported. Anchor type = ", m_anchor));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ShapeRenderer::~ShapeRenderer()
|
||||
{
|
||||
ForEachShapeInfo([](ShapeControl::ShapeInfo & info) { info.Destroy(); });
|
||||
}
|
||||
|
||||
void ShapeRenderer::Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng)
|
||||
{
|
||||
ForEachShapeInfo([context, mng](ShapeControl::ShapeInfo & info) mutable
|
||||
{ info.m_buffer->Build(context, mng->GetProgram(info.m_state.GetProgram<gpu::Program>())); });
|
||||
}
|
||||
|
||||
void ShapeRenderer::Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng,
|
||||
ScreenBase const & screen)
|
||||
{
|
||||
m2::RectD const & pxRect = screen.PixelRectIn3d();
|
||||
auto m = dp::MakeProjection(context->GetApiVersion(), 0.0f, static_cast<float>(pxRect.SizeX()),
|
||||
static_cast<float>(pxRect.SizeY()), 0.0f);
|
||||
glsl::mat4 const projection = glsl::make_mat4(m.data());
|
||||
|
||||
ForEachShapeInfo([&projection, &screen, context, mng](ShapeControl::ShapeInfo & info) mutable
|
||||
{
|
||||
if (!info.m_handle->Update(screen))
|
||||
return;
|
||||
|
||||
if (!info.m_handle->IsVisible())
|
||||
return;
|
||||
|
||||
ref_ptr<dp::GpuProgram> prg = mng->GetProgram(info.m_state.GetProgram<gpu::Program>());
|
||||
prg->Bind();
|
||||
dp::ApplyState(context, prg, info.m_state);
|
||||
|
||||
auto params = info.m_handle->GetParams();
|
||||
params.m_projection = projection;
|
||||
mng->GetParamsSetter()->Apply(context, prg, params);
|
||||
|
||||
if (info.m_handle->HasDynamicAttributes())
|
||||
{
|
||||
dp::AttributeBufferMutator mutator;
|
||||
ref_ptr<dp::AttributeBufferMutator> mutatorRef = make_ref(&mutator);
|
||||
info.m_handle->GetAttributeMutation(mutatorRef);
|
||||
info.m_buffer->ApplyMutation(context, nullptr, mutatorRef);
|
||||
}
|
||||
|
||||
info.m_buffer->Render(context, info.m_state.GetDrawAsLine());
|
||||
});
|
||||
}
|
||||
|
||||
void ShapeRenderer::AddShape(dp::RenderState const & state, drape_ptr<dp::RenderBucket> && bucket)
|
||||
{
|
||||
m_shapes.emplace_back(ShapeControl());
|
||||
m_shapes.back().AddShape(state, std::move(bucket));
|
||||
}
|
||||
|
||||
void ShapeRenderer::AddShapeControl(ShapeControl && control)
|
||||
{
|
||||
m_shapes.push_back(std::move(control));
|
||||
}
|
||||
|
||||
void ShapeRenderer::SetPivot(m2::PointF const & pivot)
|
||||
{
|
||||
for (auto & control : m_shapes)
|
||||
for (auto & info : control.m_shapesInfo)
|
||||
info.m_handle->SetPivot(glsl::ToVec2(pivot));
|
||||
}
|
||||
|
||||
void ShapeRenderer::ForEachShapeControl(TShapeControlEditFn const & fn)
|
||||
{
|
||||
std::for_each(m_shapes.begin(), m_shapes.end(), fn);
|
||||
}
|
||||
|
||||
void ShapeRenderer::ForEachShapeInfo(ShapeRenderer::TShapeInfoEditFn const & fn)
|
||||
{
|
||||
ForEachShapeControl([&fn](ShapeControl & shape)
|
||||
{ std::for_each(shape.m_shapesInfo.begin(), shape.m_shapesInfo.end(), fn); });
|
||||
}
|
||||
|
||||
ref_ptr<Handle> ShapeRenderer::ProcessTapEvent(m2::RectD const & touchArea)
|
||||
{
|
||||
ref_ptr<Handle> resultHandle = nullptr;
|
||||
ForEachShapeInfo([&resultHandle, &touchArea](ShapeControl::ShapeInfo & shapeInfo)
|
||||
{
|
||||
if (shapeInfo.m_handle->IsTapped(touchArea))
|
||||
resultHandle = make_ref(shapeInfo.m_handle);
|
||||
});
|
||||
|
||||
return resultHandle;
|
||||
}
|
||||
|
||||
ref_ptr<Handle> ShapeRenderer::FindHandle(FeatureID const & id)
|
||||
{
|
||||
ref_ptr<Handle> resultHandle = nullptr;
|
||||
ForEachShapeInfo([&resultHandle, &id](ShapeControl::ShapeInfo & shapeInfo)
|
||||
{
|
||||
if (shapeInfo.m_handle->GetOverlayID().m_featureId == id)
|
||||
resultHandle = make_ref(shapeInfo.m_handle);
|
||||
});
|
||||
|
||||
return resultHandle;
|
||||
}
|
||||
|
||||
ShapeControl::ShapeInfo::ShapeInfo(dp::RenderState const & state, drape_ptr<dp::VertexArrayBuffer> && buffer,
|
||||
drape_ptr<Handle> && handle)
|
||||
: m_state(state)
|
||||
, m_buffer(std::move(buffer))
|
||||
, m_handle(std::move(handle))
|
||||
{}
|
||||
|
||||
void ShapeControl::ShapeInfo::Destroy()
|
||||
{
|
||||
m_handle.reset();
|
||||
m_buffer.reset();
|
||||
}
|
||||
|
||||
void ShapeControl::AddShape(dp::RenderState const & state, drape_ptr<dp::RenderBucket> && bucket)
|
||||
{
|
||||
ASSERT(bucket->GetOverlayHandlesCount() == 1, ());
|
||||
|
||||
drape_ptr<dp::OverlayHandle> handle = bucket->PopOverlayHandle();
|
||||
ASSERT(dynamic_cast<Handle *>(handle.get()), ());
|
||||
|
||||
m_shapesInfo.emplace_back(state, std::move(bucket->MoveBuffer()),
|
||||
drape_ptr<Handle>(static_cast<Handle *>(handle.release())));
|
||||
}
|
||||
} // namespace gui
|
||||
121
libs/drape_frontend/gui/shape.hpp
Normal file
121
libs/drape_frontend/gui/shape.hpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape_frontend/gui/skin.hpp"
|
||||
#include "drape_frontend/render_state_extension.hpp"
|
||||
|
||||
#include "shaders/program_manager.hpp"
|
||||
|
||||
#include "drape/batcher.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/overlay_handle.hpp"
|
||||
#include "drape/texture_manager.hpp"
|
||||
#include "drape/vertex_array_buffer.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class Handle : public dp::OverlayHandle
|
||||
{
|
||||
public:
|
||||
Handle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot);
|
||||
|
||||
gpu::GuiProgramParams const & GetParams() const { return m_params; }
|
||||
|
||||
bool Update(ScreenBase const & screen) override;
|
||||
|
||||
virtual bool IsTapped(m2::RectD const & /* touchArea */) const { return false; }
|
||||
virtual void OnTapBegin() {}
|
||||
virtual void OnTap() {}
|
||||
virtual void OnTapEnd() {}
|
||||
|
||||
bool IndexesRequired() const override;
|
||||
m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const override;
|
||||
void GetPixelShape(ScreenBase const & screen, bool perspective, Rects & rects) const override;
|
||||
|
||||
virtual void SetPivot(glsl::vec2 const & pivot) { m_pivot = pivot; }
|
||||
|
||||
protected:
|
||||
gpu::GuiProgramParams m_params;
|
||||
glsl::vec2 m_pivot;
|
||||
};
|
||||
|
||||
class TappableHandle : public Handle
|
||||
{
|
||||
m2::PointF m_size;
|
||||
|
||||
public:
|
||||
TappableHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, m2::PointF size)
|
||||
: Handle(id, anchor, pivot)
|
||||
, m_size(size)
|
||||
{}
|
||||
|
||||
bool IsTapped(m2::RectD const & touchArea) const override;
|
||||
};
|
||||
|
||||
struct ShapeControl
|
||||
{
|
||||
ShapeControl() = default;
|
||||
ShapeControl(ShapeControl const & other) = delete;
|
||||
ShapeControl(ShapeControl && other) = default;
|
||||
|
||||
ShapeControl & operator=(ShapeControl const & other) = delete;
|
||||
ShapeControl & operator=(ShapeControl && other) = default;
|
||||
|
||||
struct ShapeInfo
|
||||
{
|
||||
ShapeInfo() : m_state(df::CreateRenderState(gpu::Program::TexturingGui, df::DepthLayer::GuiLayer)) {}
|
||||
ShapeInfo(dp::RenderState const & state, drape_ptr<dp::VertexArrayBuffer> && buffer, drape_ptr<Handle> && handle);
|
||||
|
||||
void Destroy();
|
||||
|
||||
dp::RenderState m_state;
|
||||
drape_ptr<dp::VertexArrayBuffer> m_buffer;
|
||||
drape_ptr<Handle> m_handle;
|
||||
};
|
||||
|
||||
void AddShape(dp::RenderState const & state, drape_ptr<dp::RenderBucket> && bucket);
|
||||
|
||||
std::vector<ShapeInfo> m_shapesInfo;
|
||||
};
|
||||
|
||||
class ShapeRenderer final
|
||||
{
|
||||
public:
|
||||
using TShapeControlEditFn = std::function<void(ShapeControl &)>;
|
||||
|
||||
~ShapeRenderer();
|
||||
|
||||
void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng);
|
||||
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, ScreenBase const & screen);
|
||||
void AddShape(dp::RenderState const & state, drape_ptr<dp::RenderBucket> && bucket);
|
||||
void AddShapeControl(ShapeControl && control);
|
||||
|
||||
void SetPivot(m2::PointF const & pivot);
|
||||
|
||||
ref_ptr<Handle> ProcessTapEvent(m2::RectD const & touchArea);
|
||||
ref_ptr<Handle> FindHandle(FeatureID const & id);
|
||||
|
||||
private:
|
||||
void ForEachShapeControl(TShapeControlEditFn const & fn);
|
||||
|
||||
using TShapeInfoEditFn = std::function<void(ShapeControl::ShapeInfo &)>;
|
||||
void ForEachShapeInfo(TShapeInfoEditFn const & fn);
|
||||
|
||||
private:
|
||||
std::vector<ShapeControl> m_shapes;
|
||||
};
|
||||
|
||||
class Shape
|
||||
{
|
||||
public:
|
||||
explicit Shape(gui::Position const & position) : m_position(position) {}
|
||||
|
||||
using TTapHandler = std::function<void()>;
|
||||
|
||||
protected:
|
||||
gui::Position m_position;
|
||||
};
|
||||
} // namespace gui
|
||||
285
libs/drape_frontend/gui/skin.cpp
Normal file
285
libs/drape_frontend/gui/skin.cpp
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#include "drape_frontend/gui/skin.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "coding/parse_xml.hpp"
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
namespace
|
||||
{
|
||||
#ifdef DEBUG
|
||||
bool IsSimple(dp::Anchor anchor)
|
||||
{
|
||||
return anchor >= 0 && anchor <= 8;
|
||||
}
|
||||
|
||||
bool IsAnchor(dp::Anchor anchor)
|
||||
{
|
||||
return anchor >= 0 && anchor <= 10;
|
||||
}
|
||||
#endif
|
||||
|
||||
dp::Anchor ParseValueAnchor(std::string_view value)
|
||||
{
|
||||
if (value == "center")
|
||||
return dp::Center;
|
||||
if (value == "left")
|
||||
return dp::Left;
|
||||
if (value == "right")
|
||||
return dp::Right;
|
||||
if (value == "top")
|
||||
return dp::Top;
|
||||
if (value == "bottom")
|
||||
return dp::Bottom;
|
||||
else
|
||||
ASSERT(false, ());
|
||||
|
||||
return dp::Center;
|
||||
}
|
||||
|
||||
dp::Anchor MergeAnchors(dp::Anchor src, dp::Anchor dst)
|
||||
{
|
||||
ASSERT(IsSimple(dst), ());
|
||||
ASSERT(IsSimple(src), ());
|
||||
dp::Anchor result = static_cast<dp::Anchor>(src | dst);
|
||||
ASSERT(IsAnchor(result), ());
|
||||
return result;
|
||||
}
|
||||
|
||||
float ParseFloat(char const * v)
|
||||
{
|
||||
float f = 0.0f;
|
||||
VERIFY(strings::to_float(v, f), ());
|
||||
return f;
|
||||
}
|
||||
|
||||
class ResolverParser
|
||||
{
|
||||
public:
|
||||
enum class Element
|
||||
{
|
||||
Empty,
|
||||
Anchor,
|
||||
Relative,
|
||||
Offset
|
||||
};
|
||||
|
||||
ResolverParser() : m_element(Element::Empty) {}
|
||||
|
||||
void Parse(std::string_view attr, char const * value)
|
||||
{
|
||||
ASSERT(m_element != Element::Empty, ());
|
||||
|
||||
if (attr == "x")
|
||||
{
|
||||
ASSERT(m_element == Element::Offset, ());
|
||||
m_resolver.SetOffsetX(ParseFloat(value));
|
||||
}
|
||||
else if (attr == "y")
|
||||
{
|
||||
ASSERT(m_element == Element::Offset, ());
|
||||
m_resolver.SetOffsetY(ParseFloat(value));
|
||||
}
|
||||
else if (attr == "vertical" || attr == "horizontal")
|
||||
{
|
||||
if (m_element == Element::Anchor)
|
||||
m_resolver.AddAnchor(ParseValueAnchor(value));
|
||||
else if (m_element == Element::Relative)
|
||||
m_resolver.AddRelative(ParseValueAnchor(value));
|
||||
else
|
||||
ASSERT(false, ());
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() { m_resolver = PositionResolver(); }
|
||||
|
||||
PositionResolver const & GetResolver() const { return m_resolver; }
|
||||
|
||||
void SetElement(Element e) { m_element = e; }
|
||||
|
||||
private:
|
||||
Element m_element;
|
||||
PositionResolver m_resolver;
|
||||
};
|
||||
|
||||
class SkinLoader
|
||||
{
|
||||
public:
|
||||
explicit SkinLoader(std::map<EWidget, std::pair<PositionResolver, PositionResolver>> & skin) : m_skin(skin) {}
|
||||
|
||||
bool Push(std::string_view element)
|
||||
{
|
||||
if (!m_inElement)
|
||||
{
|
||||
if (element == "root")
|
||||
return true;
|
||||
|
||||
m_inElement = true;
|
||||
if (element == "ruler")
|
||||
m_currentElement = WIDGET_RULER;
|
||||
else if (element == "compass")
|
||||
m_currentElement = WIDGET_COMPASS;
|
||||
else if (element == "copyright")
|
||||
m_currentElement = WIDGET_COPYRIGHT;
|
||||
else
|
||||
ASSERT(false, ());
|
||||
}
|
||||
else if (!m_inConfiguration)
|
||||
{
|
||||
if (element == "portrait" || element == "landscape")
|
||||
m_inConfiguration = true;
|
||||
else
|
||||
ASSERT(false, ());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element == "anchor")
|
||||
m_parser.SetElement(ResolverParser::Element::Anchor);
|
||||
else if (element == "relative")
|
||||
m_parser.SetElement(ResolverParser::Element::Relative);
|
||||
else if (element == "offset")
|
||||
m_parser.SetElement(ResolverParser::Element::Offset);
|
||||
else
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Pop(std::string_view element)
|
||||
{
|
||||
if (element == "anchor" || element == "relative" || element == "offset")
|
||||
m_parser.SetElement(ResolverParser::Element::Empty);
|
||||
else if (element == "portrait")
|
||||
{
|
||||
m_skin[m_currentElement].first = m_parser.GetResolver();
|
||||
m_parser.Reset();
|
||||
m_inConfiguration = false;
|
||||
}
|
||||
else if (element == "landscape")
|
||||
{
|
||||
m_skin[m_currentElement].second = m_parser.GetResolver();
|
||||
m_parser.Reset();
|
||||
m_inConfiguration = false;
|
||||
}
|
||||
else if (element == "ruler" || element == "compass" || element == "copyright" || element == "country_status")
|
||||
{
|
||||
m_inElement = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AddAttr(std::string_view attribute, char const * value) { m_parser.Parse(attribute, value); }
|
||||
|
||||
void CharData(std::string const &) {}
|
||||
|
||||
private:
|
||||
bool m_inConfiguration = false;
|
||||
bool m_inElement = false;
|
||||
|
||||
EWidget m_currentElement = WIDGET_RULER;
|
||||
ResolverParser m_parser;
|
||||
|
||||
std::map<EWidget, std::pair<PositionResolver, PositionResolver>> & m_skin;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Position PositionResolver::Resolve(int w, int h, double vs) const
|
||||
{
|
||||
float resultX = w / 2.0f;
|
||||
float resultY = h / 2.0f;
|
||||
m2::PointF offset = m_offset * vs;
|
||||
|
||||
if (m_resolveAnchor & dp::Left)
|
||||
resultX = offset.x;
|
||||
else if (m_resolveAnchor & dp::Right)
|
||||
resultX = w - offset.x;
|
||||
else
|
||||
resultX += offset.x;
|
||||
|
||||
if (m_resolveAnchor & dp::Top)
|
||||
resultY = offset.y;
|
||||
else if (m_resolveAnchor & dp::Bottom)
|
||||
resultY = h - offset.y;
|
||||
else
|
||||
resultY += offset.y;
|
||||
|
||||
return Position(m2::PointF(resultX, resultY), m_elementAnchor);
|
||||
}
|
||||
|
||||
void PositionResolver::AddAnchor(dp::Anchor anchor)
|
||||
{
|
||||
m_elementAnchor = MergeAnchors(m_elementAnchor, anchor);
|
||||
}
|
||||
|
||||
void PositionResolver::AddRelative(dp::Anchor anchor)
|
||||
{
|
||||
m_resolveAnchor = MergeAnchors(m_resolveAnchor, anchor);
|
||||
}
|
||||
|
||||
void PositionResolver::SetOffsetX(float x)
|
||||
{
|
||||
m_offset.x = x;
|
||||
}
|
||||
|
||||
void PositionResolver::SetOffsetY(float y)
|
||||
{
|
||||
m_offset.y = y;
|
||||
}
|
||||
|
||||
Skin::Skin(ReaderPtr<Reader> const & reader, float visualScale) : m_visualScale(visualScale)
|
||||
{
|
||||
SkinLoader loader(m_resolvers);
|
||||
ReaderSource<ReaderPtr<Reader>> source(reader);
|
||||
if (!ParseXML(source, loader))
|
||||
LOG(LERROR, ("Error parsing gui skin"));
|
||||
}
|
||||
|
||||
Position Skin::ResolvePosition(EWidget name)
|
||||
{
|
||||
// check that name have only one bit
|
||||
ASSERT((static_cast<int>(name) & (static_cast<int>(name) - 1)) == 0, ());
|
||||
TResolversPair const & resolvers = m_resolvers[name];
|
||||
PositionResolver const & resolver = (m_displayWidth < m_displayHeight) ? resolvers.first : resolvers.second;
|
||||
return resolver.Resolve(m_displayWidth, m_displayHeight, m_visualScale);
|
||||
}
|
||||
|
||||
void Skin::Resize(int w, int h)
|
||||
{
|
||||
m_displayWidth = w;
|
||||
m_displayHeight = h;
|
||||
}
|
||||
|
||||
ReaderPtr<Reader> ResolveGuiSkinFile(std::string const & deviceType)
|
||||
{
|
||||
Platform & pl = GetPlatform();
|
||||
std::unique_ptr<Reader> reader;
|
||||
try
|
||||
{
|
||||
reader = pl.GetReader("symbols/default/" + deviceType + ".ui");
|
||||
}
|
||||
catch (FileAbsentException & e)
|
||||
{
|
||||
LOG(LINFO, ("Gui skin for : ", deviceType, "not found"));
|
||||
}
|
||||
|
||||
if (!reader)
|
||||
{
|
||||
try
|
||||
{
|
||||
reader = pl.GetReader("symbols/default/default.ui");
|
||||
}
|
||||
catch (FileAbsentException & e)
|
||||
{
|
||||
LOG(LINFO, ("Default gui skin not found"));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return ReaderPtr<Reader>(std::move(reader));
|
||||
}
|
||||
} // namespace gui
|
||||
107
libs/drape_frontend/gui/skin.hpp
Normal file
107
libs/drape_frontend/gui/skin.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/drape_diagnostics.hpp"
|
||||
#include "drape/drape_global.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
enum EWidget
|
||||
{
|
||||
WIDGET_RULER = 0x1,
|
||||
WIDGET_COMPASS = 0x2,
|
||||
WIDGET_COPYRIGHT = 0x4,
|
||||
WIDGET_SCALE_FPS_LABEL = 0x8,
|
||||
// The following widgets are controlled by the engine. Don't use them in platform code.
|
||||
WIDGET_CHOOSE_POSITION_MARK = 0x8000,
|
||||
#ifdef RENDER_DEBUG_INFO_LABELS
|
||||
WIDGET_DEBUG_INFO
|
||||
#endif
|
||||
};
|
||||
|
||||
enum EGuiHandle
|
||||
{
|
||||
GuiHandleScaleLabel,
|
||||
GuiHandleCopyright,
|
||||
GuiHandleCompass,
|
||||
GuiHandleRuler,
|
||||
GuiHandleRulerLabel,
|
||||
GuiHandleChoosePositionMark,
|
||||
GuiHandleWatermark,
|
||||
#ifdef RENDER_DEBUG_INFO_LABELS
|
||||
GuiHandleDebugLabel = 100
|
||||
#endif
|
||||
};
|
||||
|
||||
struct Position
|
||||
{
|
||||
Position() : m_pixelPivot(m2::PointF::Zero()), m_anchor(dp::Center) {}
|
||||
|
||||
explicit Position(dp::Anchor anchor) : m_pixelPivot(m2::PointF::Zero()), m_anchor(anchor) {}
|
||||
|
||||
Position(m2::PointF const & pt, dp::Anchor anchor) : m_pixelPivot(pt), m_anchor(anchor) {}
|
||||
|
||||
m2::PointF m_pixelPivot;
|
||||
dp::Anchor m_anchor;
|
||||
};
|
||||
|
||||
// TWidgetsInitInfo - static information about gui widgets (geometry align).
|
||||
using TWidgetsInitInfo = std::map<EWidget, gui::Position>;
|
||||
// TWidgetsLayoutInfo - dynamic info. Pivot point of each widget in pixel coordinates.
|
||||
using TWidgetsLayoutInfo = std::map<EWidget, m2::PointD>;
|
||||
// TWidgetsSizeInfo - info about widget pixel sizes.
|
||||
using TWidgetsSizeInfo = TWidgetsLayoutInfo;
|
||||
|
||||
class PositionResolver
|
||||
{
|
||||
public:
|
||||
Position Resolve(int w, int h, double vs) const;
|
||||
void AddAnchor(dp::Anchor anchor);
|
||||
void AddRelative(dp::Anchor anchor);
|
||||
void SetOffsetX(float x);
|
||||
void SetOffsetY(float y);
|
||||
|
||||
private:
|
||||
dp::Anchor m_elementAnchor = dp::Center;
|
||||
dp::Anchor m_resolveAnchor = dp::Center;
|
||||
m2::PointF m_offset = m2::PointF::Zero();
|
||||
};
|
||||
|
||||
class Skin
|
||||
{
|
||||
public:
|
||||
Skin(ReaderPtr<Reader> const & reader, float visualScale);
|
||||
|
||||
// @param name - must be single flag, not combination.
|
||||
Position ResolvePosition(EWidget name);
|
||||
void Resize(int w, int h);
|
||||
int GetWidth() const { return m_displayWidth; }
|
||||
int GetHeight() const { return m_displayHeight; }
|
||||
|
||||
template <typename ToDo>
|
||||
void ForEach(ToDo todo)
|
||||
{
|
||||
for (auto const & node : m_resolvers)
|
||||
todo(node.first, ResolvePosition(node.first));
|
||||
}
|
||||
|
||||
private:
|
||||
// TResolversPair.first - Portrait (when weight < height).
|
||||
// TResolversPair.second - Landscape (when weight >= height).
|
||||
using TResolversPair = std::pair<PositionResolver, PositionResolver>;
|
||||
std::map<EWidget, TResolversPair> m_resolvers;
|
||||
|
||||
int m_displayWidth;
|
||||
int m_displayHeight;
|
||||
float m_visualScale;
|
||||
};
|
||||
|
||||
ReaderPtr<Reader> ResolveGuiSkinFile(std::string const & deviceType);
|
||||
} // namespace gui
|
||||
Loading…
Add table
Add a link
Reference in a new issue