[drape] gui skin

This commit is contained in:
ExMix 2015-02-21 20:19:33 +03:00 committed by r.kuznetsov
parent b54f85486d
commit 1b56d130db
6 changed files with 509 additions and 1 deletions

View 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>

View file

@ -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 \

View file

@ -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 \

View 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
View 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;
}
}

View 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);
}