forked from organicmaps/organicmaps-tmp
[drape] gui skin
This commit is contained in:
parent
b54f85486d
commit
1b56d130db
6 changed files with 509 additions and 1 deletions
50
data/resource-default/default.ui
Normal file
50
data/resource-default/default.ui
Normal file
|
@ -0,0 +1,50 @@
|
|||
<root>
|
||||
<ruler>
|
||||
<portrait>
|
||||
<anchor vertical="center" horizontal="right"/>
|
||||
<relative vertical="bottom" horizontal="right"/>
|
||||
<offset x="6" y="42"/>
|
||||
</portrait>
|
||||
<landscape>
|
||||
<anchor vertical="center" horizontal="right"/>
|
||||
<relative vertical="bottom" horizontal="right"/>
|
||||
<offset x="70.4" y="10.5"/>
|
||||
</landscape>
|
||||
</ruler>
|
||||
<compass>
|
||||
<portrait>
|
||||
<anchor vertical="center" horizontal="center"/>
|
||||
<relative vertical="bottom" horizontal="left"/>
|
||||
<offset x="27" y="57"/>
|
||||
</portrait>
|
||||
<landscape>
|
||||
<anchor vertical="center" horizontal="center"/>
|
||||
<relative vertical="bottom" horizontal="left"/>
|
||||
<offset x="18" y="11.4"/>
|
||||
</landscape>
|
||||
</compass>
|
||||
<copyright>
|
||||
<portrait>
|
||||
<anchor vertical="center" horizontal="right"/>
|
||||
<relative vertical="bottom" horizontal="right"/>
|
||||
<offset x="6" y="42"/>
|
||||
</portrait>
|
||||
<landscape>
|
||||
<anchor vertical="center" horizontal="right"/>
|
||||
<relative vertical="bottom" horizontal="right"/>
|
||||
<offset x="70.4" y="10.5"/>
|
||||
</landscape>
|
||||
</copyright>
|
||||
<country_status>
|
||||
<portrait>
|
||||
<anchor vertical="center" horizontal="center"/>
|
||||
<relative vertical="center" horizontal="center"/>
|
||||
<offset x="0" y="0"/>
|
||||
</portrait>
|
||||
<landscape>
|
||||
<anchor vertical="center" horizontal="center"/>
|
||||
<relative vertical="center" horizontal="center"/>
|
||||
<offset x="0" y="0"/>
|
||||
</landscape>
|
||||
</country_status>
|
||||
</root>
|
|
@ -40,6 +40,7 @@ SOURCES += \
|
|||
map_data_provider.cpp \
|
||||
user_mark_shapes.cpp \
|
||||
user_marks_provider.cpp \
|
||||
gui_skin.cpp \
|
||||
|
||||
HEADERS += \
|
||||
engine_context.hpp \
|
||||
|
@ -78,3 +79,4 @@ HEADERS += \
|
|||
map_data_provider.hpp \
|
||||
user_mark_shapes.hpp \
|
||||
user_marks_provider.hpp \
|
||||
gui_skin.hpp \
|
||||
|
|
|
@ -4,12 +4,18 @@ CONFIG += console warn_on
|
|||
CONFIG -= app_bundle
|
||||
TEMPLATE = app
|
||||
|
||||
DEPENDENCIES = drape_frontend drape base fribidi
|
||||
DEPENDENCIES = drape_frontend coding platform drape base fribidi expat
|
||||
ROOT_DIR = ../..
|
||||
include($$ROOT_DIR/common.pri)
|
||||
|
||||
macx-* {
|
||||
LIBS *= "-framework CoreLocation" "-framework Foundation" "-framework CoreWLAN" \
|
||||
"-framework QuartzCore" "-framework IOKit"
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
../../testing/testingmain.cpp \
|
||||
memory_feature_index_tests.cpp \
|
||||
fribidi_tests.cpp \
|
||||
object_pool_tests.cpp \
|
||||
gui_skin_tests.cpp \
|
||||
|
|
60
drape_frontend/drape_frontend_tests/gui_skin_tests.cpp
Normal file
60
drape_frontend/drape_frontend_tests/gui_skin_tests.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "../../base/SRC_FIRST.hpp"
|
||||
#include "../../testing/testing.hpp"
|
||||
|
||||
#include "../gui_skin.hpp"
|
||||
#include "../visual_params.hpp"
|
||||
|
||||
UNIT_TEST(ParseDefaultSkinTest)
|
||||
{
|
||||
df::VisualParams::Init(2.0f, 1024);
|
||||
df::GuiSkin skin(df::ResolveGuiSkinFile("default"));
|
||||
float width = 600.0f;
|
||||
float height = 800.0f;
|
||||
skin.Resize(width, height);
|
||||
|
||||
df::GuiPosition compassPos = skin.ResolvePosition(df::GuiSkin::GuiElement::Compass);
|
||||
TEST_EQUAL(compassPos.m_anchor, dp::Center, ());
|
||||
TEST_ALMOST_EQUAL(compassPos.m_pixelPivot.x, 27.0f * 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(compassPos.m_pixelPivot.y, height - 57.0f * 2.0f, ());
|
||||
|
||||
df::GuiPosition rulerPos = skin.ResolvePosition(df::GuiSkin::GuiElement::Ruler);
|
||||
TEST_EQUAL(rulerPos.m_anchor, dp::Right, ());
|
||||
TEST_ALMOST_EQUAL(rulerPos.m_pixelPivot.x, width - 6.0f * 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(rulerPos.m_pixelPivot.y, height - 42.0f * 2.0f, ());
|
||||
|
||||
df::GuiPosition copyRightPos = skin.ResolvePosition(df::GuiSkin::GuiElement::Copyright);
|
||||
TEST_EQUAL(copyRightPos.m_anchor, dp::Right, ());
|
||||
TEST_ALMOST_EQUAL(copyRightPos.m_pixelPivot.x, width - 6.0f * 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(copyRightPos.m_pixelPivot.y, height - 42.0f * 2.0f, ());
|
||||
|
||||
df::GuiPosition countryStatusPos = skin.ResolvePosition(df::GuiSkin::GuiElement::CountryStatus);
|
||||
TEST_EQUAL(countryStatusPos.m_anchor, dp::Center, ());
|
||||
TEST_ALMOST_EQUAL(countryStatusPos.m_pixelPivot.x, width / 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(countryStatusPos.m_pixelPivot.y, height / 2.0f, ());
|
||||
|
||||
{
|
||||
width = 800.0f;
|
||||
height = 600.0f;
|
||||
skin.Resize(width, height);
|
||||
|
||||
df::GuiPosition compassPos = skin.ResolvePosition(df::GuiSkin::GuiElement::Compass);
|
||||
TEST_EQUAL(compassPos.m_anchor, dp::Center, ());
|
||||
TEST_ALMOST_EQUAL(compassPos.m_pixelPivot.x, 18.0f * 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(compassPos.m_pixelPivot.y, height - 11.4f * 2.0f, ());
|
||||
|
||||
df::GuiPosition rulerPos = skin.ResolvePosition(df::GuiSkin::GuiElement::Ruler);
|
||||
TEST_EQUAL(rulerPos.m_anchor, dp::Right, ());
|
||||
TEST_ALMOST_EQUAL(rulerPos.m_pixelPivot.x, width - 70.4f * 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(rulerPos.m_pixelPivot.y, height - 10.5f * 2.0f, ());
|
||||
|
||||
df::GuiPosition copyRightPos = skin.ResolvePosition(df::GuiSkin::GuiElement::Copyright);
|
||||
TEST_EQUAL(copyRightPos.m_anchor, dp::Right, ());
|
||||
TEST_ALMOST_EQUAL(copyRightPos.m_pixelPivot.x, width - 70.4f * 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(copyRightPos.m_pixelPivot.y, height - 10.5f * 2.0f, ());
|
||||
|
||||
df::GuiPosition countryStatusPos = skin.ResolvePosition(df::GuiSkin::GuiElement::CountryStatus);
|
||||
TEST_EQUAL(countryStatusPos.m_anchor, dp::Center, ());
|
||||
TEST_ALMOST_EQUAL(countryStatusPos.m_pixelPivot.x, width / 2.0f, ());
|
||||
TEST_ALMOST_EQUAL(countryStatusPos.m_pixelPivot.y, height / 2.0f, ());
|
||||
}
|
||||
}
|
320
drape_frontend/gui_skin.cpp
Normal file
320
drape_frontend/gui_skin.cpp
Normal file
|
@ -0,0 +1,320 @@
|
|||
#include "gui_skin.hpp"
|
||||
#include "visual_params.hpp"
|
||||
|
||||
#include "../base/string_utils.hpp"
|
||||
#include "../coding/parse_xml.hpp"
|
||||
#include "../platform/platform.hpp"
|
||||
|
||||
#include "../std/function.hpp"
|
||||
|
||||
namespace df
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool IsSimple(dp::Anchor anchor)
|
||||
{
|
||||
return anchor >= 0 && anchor <= 8;
|
||||
}
|
||||
|
||||
bool IsAnchor(dp::Anchor anchor)
|
||||
{
|
||||
return anchor >= 0 && anchor <= 10;
|
||||
}
|
||||
|
||||
dp::Anchor ParseValueAnchor(string const & value)
|
||||
{
|
||||
if (value == "center")
|
||||
return dp::Center;
|
||||
else if (value == "left")
|
||||
return dp::Left;
|
||||
else if (value == "right")
|
||||
return dp::Right;
|
||||
else if (value == "top")
|
||||
return dp::Top;
|
||||
else 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(string const & v)
|
||||
{
|
||||
double d = 0.0f;
|
||||
VERIFY(strings::to_double(v, d), ());
|
||||
return d;
|
||||
}
|
||||
|
||||
class ResolverParser
|
||||
{
|
||||
public:
|
||||
enum class Element
|
||||
{
|
||||
Empty,
|
||||
Anchor,
|
||||
Relative,
|
||||
Offset
|
||||
};
|
||||
|
||||
ResolverParser()
|
||||
: m_element(Element::Empty) {}
|
||||
|
||||
void Parse(string const & attr, string 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")
|
||||
{
|
||||
if (m_element == Element::Anchor)
|
||||
m_resolver.SetAnchorVertical(ParseValueAnchor(value));
|
||||
else if (m_element == Element::Relative)
|
||||
m_resolver.SetRelativeVertical(ParseValueAnchor(value));
|
||||
else
|
||||
ASSERT(false, ());
|
||||
}
|
||||
else if (attr == "horizontal")
|
||||
{
|
||||
if (m_element == Element::Anchor)
|
||||
m_resolver.SetAnchorHorizontal(ParseValueAnchor(value));
|
||||
else if (m_element == Element::Relative)
|
||||
m_resolver.SetRelativeHorizontal(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:
|
||||
SkinLoader(map<GuiSkin::GuiElement, pair<PositionResolver, PositionResolver> > & skin)
|
||||
: m_skin(skin) {}
|
||||
|
||||
bool Push(string const & element)
|
||||
{
|
||||
if (m_inElement == false)
|
||||
{
|
||||
if (element == "root")
|
||||
return true;
|
||||
|
||||
m_inElement = true;
|
||||
if (element == "ruler")
|
||||
m_currentElement = GuiSkin::GuiElement::Ruler;
|
||||
else if (element == "compass")
|
||||
m_currentElement = GuiSkin::GuiElement::Compass;
|
||||
else if (element == "copyright")
|
||||
m_currentElement = GuiSkin::GuiElement::Copyright;
|
||||
else if (element == "country_status")
|
||||
m_currentElement = GuiSkin::GuiElement::CountryStatus;
|
||||
else
|
||||
ASSERT(false, ());
|
||||
}
|
||||
else if (m_inConfiguration == false)
|
||||
{
|
||||
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(string const & 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(string const & attribute, string const & value)
|
||||
{
|
||||
m_parser.Parse(attribute, value);
|
||||
}
|
||||
|
||||
void CharData(string const &) {}
|
||||
|
||||
private:
|
||||
bool m_inConfiguration = false;
|
||||
bool m_inElement = false;
|
||||
|
||||
GuiSkin::GuiElement m_currentElement = GuiSkin::GuiElement::Ruler;
|
||||
ResolverParser m_parser;
|
||||
|
||||
map<GuiSkin::GuiElement, pair<PositionResolver, PositionResolver> > & m_skin;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
GuiPosition PositionResolver::Resolve(int w, int h) const
|
||||
{
|
||||
float resultX = w / 2.0f;
|
||||
float resultY = h / 2.0f;
|
||||
m2::PointF offset = m_offset * df::VisualParams::Instance().GetVisualScale();
|
||||
|
||||
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 GuiPosition(m2::PointF(resultX, resultY), m_elementAnchor);
|
||||
}
|
||||
|
||||
void PositionResolver::SetAnchorVertical(dp::Anchor anchor)
|
||||
{
|
||||
m_elementAnchor = MergeAnchors(m_elementAnchor, anchor);
|
||||
}
|
||||
|
||||
void PositionResolver::SetAnchorHorizontal(dp::Anchor anchor)
|
||||
{
|
||||
m_elementAnchor = MergeAnchors(m_elementAnchor, anchor);
|
||||
}
|
||||
|
||||
void PositionResolver::SetRelativeVertical(dp::Anchor anchor)
|
||||
{
|
||||
m_resolveAnchor = MergeAnchors(m_resolveAnchor, anchor);
|
||||
}
|
||||
|
||||
void PositionResolver::SetRelativeHorizontal(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;
|
||||
}
|
||||
|
||||
GuiSkin::GuiSkin(ReaderPtr<Reader> const & reader)
|
||||
{
|
||||
SkinLoader loader(m_resolvers);
|
||||
ReaderSource<ReaderPtr<Reader> > source(reader);
|
||||
if (!ParseXML(source, loader))
|
||||
LOG(LERROR, ("Error parsing gui skin"));
|
||||
}
|
||||
|
||||
GuiPosition GuiSkin::ResolvePosition(GuiElement name)
|
||||
{
|
||||
TResolversPair const & resolvers = m_resolvers[name];
|
||||
PositionResolver const & resolver = (m_displayWidth < m_displayHeight) ? resolvers.first : resolvers.second;
|
||||
return resolver.Resolve(m_displayWidth, m_displayHeight);
|
||||
}
|
||||
|
||||
void GuiSkin::Resize(int w, int h)
|
||||
{
|
||||
m_displayWidth = w;
|
||||
m_displayHeight = h;
|
||||
}
|
||||
|
||||
ReaderPtr<Reader> ResolveGuiSkinFile(string const & deviceType)
|
||||
{
|
||||
Platform & pl = GetPlatform();
|
||||
ReaderPtr<Reader> reader;
|
||||
try
|
||||
{
|
||||
reader = pl.GetReader("resource-default/" + deviceType + ".ui");
|
||||
}
|
||||
catch(FileAbsentException & e)
|
||||
{
|
||||
LOG(LINFO, ("Gui skin for : ", deviceType ,"not found"));
|
||||
}
|
||||
|
||||
if (reader.GetPtr() == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
reader = pl.GetReader("resource-default/default.ui");
|
||||
}
|
||||
catch(FileAbsentException & e)
|
||||
{
|
||||
LOG(LINFO, ("Default gui skin not found"));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
}
|
70
drape_frontend/gui_skin.hpp
Normal file
70
drape_frontend/gui_skin.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "../coding/reader.hpp"
|
||||
|
||||
#include "../drape/drape_global.hpp"
|
||||
#include "../geometry/point2d.hpp"
|
||||
|
||||
#include "../std/map.hpp"
|
||||
|
||||
namespace df
|
||||
{
|
||||
|
||||
struct GuiPosition
|
||||
{
|
||||
GuiPosition(m2::PointF const & pt, dp::Anchor anchor)
|
||||
: m_pixelPivot(pt)
|
||||
, m_anchor(anchor) {}
|
||||
|
||||
m2::PointF const m_pixelPivot;
|
||||
dp::Anchor const m_anchor;
|
||||
};
|
||||
|
||||
class PositionResolver
|
||||
{
|
||||
public:
|
||||
PositionResolver() = default;
|
||||
|
||||
GuiPosition Resolve(int w, int h) const;
|
||||
void SetAnchorVertical(dp::Anchor anchor);
|
||||
void SetAnchorHorizontal(dp::Anchor anchor);
|
||||
void SetRelativeVertical(dp::Anchor anchor);
|
||||
void SetRelativeHorizontal(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 GuiSkin
|
||||
{
|
||||
public:
|
||||
enum class GuiElement
|
||||
{
|
||||
CountryStatus,
|
||||
Ruler,
|
||||
Compass,
|
||||
Copyright
|
||||
};
|
||||
|
||||
GuiSkin(ReaderPtr<Reader> const & reader);
|
||||
|
||||
GuiPosition ResolvePosition(GuiElement name);
|
||||
void Resize(int w, int h);
|
||||
|
||||
private:
|
||||
/// TResolversPair.first - Portrait (when weight < height)
|
||||
/// TResolversPair.second - Landscape (when weight >= height)
|
||||
typedef pair<PositionResolver, PositionResolver> TResolversPair;
|
||||
map<GuiElement, TResolversPair> m_resolvers;
|
||||
|
||||
int m_displayWidth;
|
||||
int m_displayHeight;
|
||||
};
|
||||
|
||||
ReaderPtr<Reader> ResolveGuiSkinFile(string const & deviceType);
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue