Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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