mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-29 17:45:38 +00:00
Merge pull request #493 from jrassa/date-time
create new DateTimeComponent
This commit is contained in:
commit
e59e63828b
28
THEMES.md
28
THEMES.md
|
@ -731,14 +731,38 @@ EmulationStation borrows the concept of "nine patches" from Android (or "9-Slice
|
|||
- z-index value for component. Components will be rendered in order of z-index value from low to high.
|
||||
|
||||
#### datetime
|
||||
|
||||
* `pos` - type: NORMALIZED_PAIR.
|
||||
* `size` - type: NORMALIZED_PAIR.
|
||||
- You should probably not set this. Leave it to `fontSize`.
|
||||
- Possible combinations:
|
||||
- `0 0` - automatically size so text fits on one line (expanding horizontally).
|
||||
- `w 0` - automatically wrap text so it doesn't go beyond `w` (expanding vertically).
|
||||
- `w h` - works like a "text box." If `h` is non-zero and `h` <= `fontSize` (implying it should be a single line of text), text that goes beyond `w` will be truncated with an elipses (...).
|
||||
* `origin` - type: NORMALIZED_PAIR.
|
||||
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the component exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themable, "ORIGIN" is implied.
|
||||
* `rotation` - type: FLOAT.
|
||||
- angle in degrees that the text should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise.
|
||||
* `rotationOrigin` - type: NORMALIZED_PAIR.
|
||||
- Point around which the text will be rotated. Defaults to `0.5 0.5`.
|
||||
* `color` - type: COLOR.
|
||||
* `backgroundColor` - type: COLOR;
|
||||
* `fontPath` - type: PATH.
|
||||
- Path to a truetype font (.ttf).
|
||||
* `fontSize` - type: FLOAT.
|
||||
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height).
|
||||
* `alignment` - type: STRING.
|
||||
- Valid values are "left", "center", or "right". Controls alignment on the X axis. "center" will also align vertically.
|
||||
* `forceUppercase` - type: BOOLEAN. Draw text in uppercase.
|
||||
* `lineSpacing` - type: FLOAT. Controls the space between lines (as a multiple of font height). Default is 1.5.
|
||||
* `zIndex` - type: FLOAT.
|
||||
- z-index value for component. Components will be rendered in order of z-index value from low to high.
|
||||
* `displayRelative` - type: BOOLEAN. Renders the datetime as a a relative string (ex: 'x days ago')
|
||||
* `format` - type: STRING. Specifies format for rendering datetime.
|
||||
- %Y: The year, including the century (1900)
|
||||
- %m: The month number [01,12]
|
||||
- %d: The day of the month [01,31]
|
||||
- %H: The hour (24-hour clock) [00,23]
|
||||
- %M: The minute [00,59]
|
||||
- %S: The second [00,59]
|
||||
|
||||
#### sound
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "components/ScraperSearchComponent.h"
|
||||
|
||||
#include "components/ComponentList.h"
|
||||
#include "components/DateTimeComponent.h"
|
||||
#include "components/DateTimeEditComponent.h"
|
||||
#include "components/ImageComponent.h"
|
||||
#include "components/RatingComponent.h"
|
||||
#include "components/ScrollableContainer.h"
|
||||
|
@ -43,7 +43,7 @@ ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type)
|
|||
const unsigned int mdColor = 0x777777FF;
|
||||
const unsigned int mdLblColor = 0x666666FF;
|
||||
mMD_Rating = std::make_shared<RatingComponent>(mWindow);
|
||||
mMD_ReleaseDate = std::make_shared<DateTimeComponent>(mWindow);
|
||||
mMD_ReleaseDate = std::make_shared<DateTimeEditComponent>(mWindow);
|
||||
mMD_ReleaseDate->setColor(mdColor);
|
||||
mMD_Developer = std::make_shared<TextComponent>(mWindow, "", font, mdColor);
|
||||
mMD_Publisher = std::make_shared<TextComponent>(mWindow, "", font, mdColor);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "GuiComponent.h"
|
||||
|
||||
class ComponentList;
|
||||
class DateTimeComponent;
|
||||
class DateTimeEditComponent;
|
||||
class ImageComponent;
|
||||
class RatingComponent;
|
||||
class ScrollableContainer;
|
||||
|
@ -69,7 +69,7 @@ private:
|
|||
|
||||
std::shared_ptr<ComponentGrid> mMD_Grid;
|
||||
std::shared_ptr<RatingComponent> mMD_Rating;
|
||||
std::shared_ptr<DateTimeComponent> mMD_ReleaseDate;
|
||||
std::shared_ptr<DateTimeEditComponent> mMD_ReleaseDate;
|
||||
std::shared_ptr<TextComponent> mMD_Developer;
|
||||
std::shared_ptr<TextComponent> mMD_Publisher;
|
||||
std::shared_ptr<TextComponent> mMD_Genre;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "components/ButtonComponent.h"
|
||||
#include "components/ComponentList.h"
|
||||
#include "components/DateTimeComponent.h"
|
||||
#include "components/DateTimeEditComponent.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "components/RatingComponent.h"
|
||||
#include "components/SwitchComponent.h"
|
||||
|
@ -87,21 +87,21 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector
|
|||
}
|
||||
case MD_DATE:
|
||||
{
|
||||
ed = std::make_shared<DateTimeComponent>(window);
|
||||
ed = std::make_shared<DateTimeEditComponent>(window);
|
||||
row.addElement(ed, false);
|
||||
|
||||
auto spacer = std::make_shared<GuiComponent>(mWindow);
|
||||
spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0);
|
||||
row.addElement(spacer, false);
|
||||
|
||||
// pass input to the actual DateTimeComponent instead of the spacer
|
||||
// pass input to the actual DateTimeEditComponent instead of the spacer
|
||||
row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, std::placeholders::_2);
|
||||
|
||||
break;
|
||||
}
|
||||
case MD_TIME:
|
||||
{
|
||||
ed = std::make_shared<DateTimeComponent>(window, DateTimeComponent::DISP_RELATIVE_TO_NOW);
|
||||
ed = std::make_shared<DateTimeEditComponent>(window, DateTimeEditComponent::DISP_RELATIVE_TO_NOW);
|
||||
row.addElement(ed, false);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
|||
addChild(&mPlayers);
|
||||
mLblLastPlayed.setText("Last played: ");
|
||||
addChild(&mLblLastPlayed);
|
||||
mLastPlayed.setDisplayMode(DateTimeComponent::DISP_RELATIVE_TO_NOW);
|
||||
mLastPlayed.setDisplayRelative(true);
|
||||
addChild(&mLastPlayed);
|
||||
mLblPlayCount.setText("Times played: ");
|
||||
addChild(&mLblPlayCount);
|
||||
|
|
|
@ -49,7 +49,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) :
|
|||
addChild(&mPlayers);
|
||||
mLblLastPlayed.setText("Last played: ");
|
||||
addChild(&mLblLastPlayed);
|
||||
mLastPlayed.setDisplayMode(DateTimeComponent::DISP_RELATIVE_TO_NOW);
|
||||
mLastPlayed.setDisplayRelative(true);
|
||||
addChild(&mLastPlayed);
|
||||
mLblPlayCount.setText("Times played: ");
|
||||
addChild(&mLblPlayCount);
|
||||
|
|
|
@ -86,7 +86,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) :
|
|||
addChild(&mPlayers);
|
||||
mLblLastPlayed.setText("Last played: ");
|
||||
addChild(&mLblLastPlayed);
|
||||
mLastPlayed.setDisplayMode(DateTimeComponent::DISP_RELATIVE_TO_NOW);
|
||||
mLastPlayed.setDisplayRelative(true);
|
||||
addChild(&mLastPlayed);
|
||||
mLblPlayCount.setText("Times played: ");
|
||||
addChild(&mLblPlayCount);
|
||||
|
|
|
@ -32,6 +32,7 @@ set(CORE_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentList.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeEditComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/HelpComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GridTileComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/IList.h
|
||||
|
@ -106,6 +107,7 @@ set(CORE_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentList.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeEditComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/HelpComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GridTileComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.cpp
|
||||
|
|
|
@ -90,10 +90,19 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>> The
|
|||
{ "datetime", {
|
||||
{ "pos", NORMALIZED_PAIR },
|
||||
{ "size", NORMALIZED_PAIR },
|
||||
{ "color", COLOR },
|
||||
{ "origin", NORMALIZED_PAIR },
|
||||
{ "rotation", FLOAT },
|
||||
{ "rotationOrigin", NORMALIZED_PAIR },
|
||||
{ "backgroundColor", COLOR },
|
||||
{ "fontPath", PATH },
|
||||
{ "fontSize", FLOAT },
|
||||
{ "color", COLOR },
|
||||
{ "alignment", STRING },
|
||||
{ "forceUppercase", BOOLEAN },
|
||||
{ "lineSpacing", FLOAT },
|
||||
{ "value", STRING },
|
||||
{ "format", STRING },
|
||||
{ "displayRelative", BOOLEAN },
|
||||
{ "zIndex", FLOAT } } },
|
||||
{ "rating", {
|
||||
{ "pos", NORMALIZED_PAIR },
|
||||
|
|
|
@ -1,176 +1,25 @@
|
|||
#include "components/DateTimeComponent.h"
|
||||
|
||||
#include "resources/Font.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "Log.h"
|
||||
#include "Renderer.h"
|
||||
#include "Settings.h"
|
||||
|
||||
DateTimeComponent::DateTimeComponent(Window* window, DisplayMode dispMode) : GuiComponent(window),
|
||||
mEditing(false), mEditIndex(0), mDisplayMode(dispMode), mRelativeUpdateAccumulator(0),
|
||||
mColor(0x777777FF), mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT)), mUppercase(false), mAutoSize(true)
|
||||
DateTimeComponent::DateTimeComponent(Window* window) : TextComponent(window), mDisplayRelative(false)
|
||||
{
|
||||
updateTextCache();
|
||||
setFormat("%m/%d/%Y");
|
||||
}
|
||||
|
||||
void DateTimeComponent::setDisplayMode(DisplayMode mode)
|
||||
DateTimeComponent::DateTimeComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color, Alignment align,
|
||||
Vector3f pos, Vector2f size, unsigned int bgcolor) : TextComponent(window, text, font, color, align, pos, size, bgcolor), mDisplayRelative(false)
|
||||
{
|
||||
mDisplayMode = mode;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
bool DateTimeComponent::input(InputConfig* config, Input input)
|
||||
{
|
||||
if(input.value == 0)
|
||||
return false;
|
||||
|
||||
if(config->isMappedTo("a", input))
|
||||
{
|
||||
if(mDisplayMode != DISP_RELATIVE_TO_NOW) //don't allow editing for relative times
|
||||
mEditing = !mEditing;
|
||||
|
||||
if(mEditing)
|
||||
{
|
||||
//started editing
|
||||
mTimeBeforeEdit = mTime;
|
||||
|
||||
//initialize to now if unset
|
||||
if(mTime.getTime() == Utils::Time::NOT_A_DATE_TIME)
|
||||
{
|
||||
mTime = Utils::Time::now();
|
||||
updateTextCache();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(mEditing)
|
||||
{
|
||||
if(config->isMappedTo("b", input))
|
||||
{
|
||||
mEditing = false;
|
||||
mTime = mTimeBeforeEdit;
|
||||
updateTextCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
int incDir = 0;
|
||||
if(config->isMappedTo("up", input) || config->isMappedTo("pageup", input))
|
||||
incDir = 1;
|
||||
else if(config->isMappedTo("down", input) || config->isMappedTo("pagedown", input))
|
||||
incDir = -1;
|
||||
|
||||
if(incDir != 0)
|
||||
{
|
||||
tm new_tm = mTime;
|
||||
|
||||
if(mEditIndex == 0)
|
||||
{
|
||||
new_tm.tm_mon += incDir;
|
||||
|
||||
if(new_tm.tm_mon > 11)
|
||||
new_tm.tm_mon = 0;
|
||||
else if(new_tm.tm_mon < 0)
|
||||
new_tm.tm_mon = 11;
|
||||
|
||||
}
|
||||
else if(mEditIndex == 1)
|
||||
{
|
||||
const int days_in_month = Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
||||
new_tm.tm_mday += incDir;
|
||||
|
||||
if(new_tm.tm_mday > days_in_month)
|
||||
new_tm.tm_mday = 1;
|
||||
else if(new_tm.tm_mday < 1)
|
||||
new_tm.tm_mday = days_in_month;
|
||||
|
||||
}
|
||||
else if(mEditIndex == 2)
|
||||
{
|
||||
new_tm.tm_year += incDir;
|
||||
|
||||
if(new_tm.tm_year < 0)
|
||||
new_tm.tm_year = 0;
|
||||
}
|
||||
|
||||
//validate day
|
||||
const int days_in_month = Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
||||
if(new_tm.tm_mday > days_in_month)
|
||||
new_tm.tm_mday = days_in_month;
|
||||
|
||||
mTime = new_tm;
|
||||
|
||||
updateTextCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(config->isMappedTo("right", input))
|
||||
{
|
||||
mEditIndex++;
|
||||
if(mEditIndex >= (int)mCursorBoxes.size())
|
||||
mEditIndex--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(config->isMappedTo("left", input))
|
||||
{
|
||||
mEditIndex--;
|
||||
if(mEditIndex < 0)
|
||||
mEditIndex++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return GuiComponent::input(config, input);
|
||||
}
|
||||
|
||||
void DateTimeComponent::update(int deltaTime)
|
||||
{
|
||||
if(mDisplayMode == DISP_RELATIVE_TO_NOW)
|
||||
{
|
||||
mRelativeUpdateAccumulator += deltaTime;
|
||||
if(mRelativeUpdateAccumulator > 1000)
|
||||
{
|
||||
mRelativeUpdateAccumulator = 0;
|
||||
updateTextCache();
|
||||
}
|
||||
}
|
||||
|
||||
GuiComponent::update(deltaTime);
|
||||
}
|
||||
|
||||
void DateTimeComponent::render(const Transform4x4f& parentTrans)
|
||||
{
|
||||
Transform4x4f trans = parentTrans * getTransform();
|
||||
|
||||
if(mTextCache)
|
||||
{
|
||||
// vertically center
|
||||
Vector3f off(0, (mSize.y() - mTextCache->metrics.size.y()) / 2, 0);
|
||||
trans.translate(off);
|
||||
trans.round();
|
||||
|
||||
Renderer::setMatrix(trans);
|
||||
|
||||
std::shared_ptr<Font> font = getFont();
|
||||
|
||||
mTextCache->setColor((mColor & 0xFFFFFF00) | getOpacity());
|
||||
font->renderTextCache(mTextCache.get());
|
||||
|
||||
if(mEditing)
|
||||
{
|
||||
if(mEditIndex >= 0 && (unsigned int)mEditIndex < mCursorBoxes.size())
|
||||
{
|
||||
Renderer::drawRect((int)mCursorBoxes[mEditIndex][0], (int)mCursorBoxes[mEditIndex][1],
|
||||
(int)mCursorBoxes[mEditIndex][2], (int)mCursorBoxes[mEditIndex][3], 0x00000022);
|
||||
}
|
||||
}
|
||||
}
|
||||
setFormat("%m/%d/%Y");
|
||||
}
|
||||
|
||||
void DateTimeComponent::setValue(const std::string& val)
|
||||
{
|
||||
mTime = val;
|
||||
updateTextCache();
|
||||
onTextChanged();
|
||||
}
|
||||
|
||||
std::string DateTimeComponent::getValue() const
|
||||
|
@ -178,33 +27,28 @@ std::string DateTimeComponent::getValue() const
|
|||
return mTime;
|
||||
}
|
||||
|
||||
DateTimeComponent::DisplayMode DateTimeComponent::getCurrentDisplayMode() const
|
||||
void DateTimeComponent::setFormat(const std::string& format)
|
||||
{
|
||||
/*if(mEditing)
|
||||
{
|
||||
if(mDisplayMode == DISP_RELATIVE_TO_NOW)
|
||||
{
|
||||
//TODO: if time component == 00:00:00, return DISP_DATE, else return DISP_DATE_TIME
|
||||
return DISP_DATE;
|
||||
}
|
||||
}*/
|
||||
|
||||
return mDisplayMode;
|
||||
mFormat = format;
|
||||
onTextChanged();
|
||||
}
|
||||
|
||||
std::string DateTimeComponent::getDisplayString(DisplayMode mode) const
|
||||
void DateTimeComponent::setDisplayRelative(bool displayRelative)
|
||||
{
|
||||
std::string fmt;
|
||||
switch(mode)
|
||||
mDisplayRelative = displayRelative;
|
||||
onTextChanged();
|
||||
}
|
||||
|
||||
void DateTimeComponent::onTextChanged()
|
||||
{
|
||||
case DISP_DATE:
|
||||
fmt = "%m/%d/%Y";
|
||||
break;
|
||||
case DISP_DATE_TIME:
|
||||
fmt = "%m/%d/%Y %H:%M:%S";
|
||||
break;
|
||||
case DISP_RELATIVE_TO_NOW:
|
||||
mText = getDisplayString();
|
||||
|
||||
TextComponent::onTextChanged();
|
||||
}
|
||||
|
||||
std::string DateTimeComponent::getDisplayString() const
|
||||
{
|
||||
if (mDisplayRelative) {
|
||||
//relative time
|
||||
if(mTime.getTime() == 0)
|
||||
return "never";
|
||||
|
@ -225,112 +69,62 @@ std::string DateTimeComponent::getDisplayString(DisplayMode mode) const
|
|||
|
||||
return std::string(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(mTime.getTime() == 0)
|
||||
return "unknown";
|
||||
|
||||
return Utils::Time::timeToString(mTime, fmt);
|
||||
return Utils::Time::timeToString(mTime.getTime(), mFormat);
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> DateTimeComponent::getFont() const
|
||||
void DateTimeComponent::render(const Transform4x4f& parentTrans)
|
||||
{
|
||||
if(mFont)
|
||||
return mFont;
|
||||
|
||||
return Font::get(FONT_SIZE_MEDIUM);
|
||||
TextComponent::render(parentTrans);
|
||||
}
|
||||
|
||||
void DateTimeComponent::updateTextCache()
|
||||
{
|
||||
DisplayMode mode = getCurrentDisplayMode();
|
||||
const std::string dispString = mUppercase ? Utils::String::toUpper(getDisplayString(mode)) : getDisplayString(mode);
|
||||
std::shared_ptr<Font> font = getFont();
|
||||
mTextCache = std::unique_ptr<TextCache>(font->buildTextCache(dispString, 0, 0, mColor));
|
||||
|
||||
if(mAutoSize)
|
||||
{
|
||||
mSize = mTextCache->metrics.size;
|
||||
|
||||
mAutoSize = false;
|
||||
if(getParent())
|
||||
getParent()->onSizeChanged();
|
||||
}
|
||||
|
||||
//set up cursor positions
|
||||
mCursorBoxes.clear();
|
||||
|
||||
if(dispString.empty() || mode == DISP_RELATIVE_TO_NOW)
|
||||
return;
|
||||
|
||||
//month
|
||||
Vector2f start(0, 0);
|
||||
Vector2f end = font->sizeText(dispString.substr(0, 2));
|
||||
Vector2f diff = end - start;
|
||||
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
|
||||
|
||||
//day
|
||||
start[0] = font->sizeText(dispString.substr(0, 3)).x();
|
||||
end = font->sizeText(dispString.substr(0, 5));
|
||||
diff = end - start;
|
||||
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
|
||||
|
||||
//year
|
||||
start[0] = font->sizeText(dispString.substr(0, 6)).x();
|
||||
end = font->sizeText(dispString.substr(0, 10));
|
||||
diff = end - start;
|
||||
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
|
||||
|
||||
//if mode == DISP_DATE_TIME do times too but I don't wanna do the logic for editing times because no one will ever use it so screw it
|
||||
}
|
||||
|
||||
void DateTimeComponent::setColor(unsigned int color)
|
||||
{
|
||||
mColor = color;
|
||||
if(mTextCache)
|
||||
mTextCache->setColor(color);
|
||||
}
|
||||
|
||||
void DateTimeComponent::setFont(std::shared_ptr<Font> font)
|
||||
{
|
||||
mFont = font;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
void DateTimeComponent::onSizeChanged()
|
||||
{
|
||||
mAutoSize = false;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
void DateTimeComponent::setUppercase(bool uppercase)
|
||||
{
|
||||
mUppercase = uppercase;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
||||
{
|
||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime");
|
||||
if(!elem)
|
||||
return;
|
||||
|
||||
// We set mAutoSize BEFORE calling GuiComponent::applyTheme because it calls
|
||||
// setSize(), which will call updateTextCache(), which will reset mSize if
|
||||
// mAutoSize == true, ignoring the theme's value.
|
||||
if(properties & ThemeFlags::SIZE)
|
||||
mAutoSize = !elem->has("size");
|
||||
|
||||
GuiComponent::applyTheme(theme, view, element, properties);
|
||||
|
||||
using namespace ThemeFlags;
|
||||
|
||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime");
|
||||
if(!elem)
|
||||
return;
|
||||
|
||||
if(elem->has("displayRelative"))
|
||||
setDisplayRelative(elem->get<bool>("displayRelative"));
|
||||
|
||||
if(elem->has("format"))
|
||||
setFormat(elem->get<std::string>("format"));
|
||||
|
||||
if (properties & COLOR && elem->has("color"))
|
||||
setColor(elem->get<unsigned int>("color"));
|
||||
|
||||
setRenderBackground(false);
|
||||
if (properties & COLOR && elem->has("backgroundColor")) {
|
||||
setBackgroundColor(elem->get<unsigned int>("backgroundColor"));
|
||||
setRenderBackground(true);
|
||||
}
|
||||
|
||||
if(properties & ALIGNMENT && elem->has("alignment"))
|
||||
{
|
||||
std::string str = elem->get<std::string>("alignment");
|
||||
if(str == "left")
|
||||
setHorizontalAlignment(ALIGN_LEFT);
|
||||
else if(str == "center")
|
||||
setHorizontalAlignment(ALIGN_CENTER);
|
||||
else if(str == "right")
|
||||
setHorizontalAlignment(ALIGN_RIGHT);
|
||||
else
|
||||
LOG(LogError) << "Unknown text alignment string: " << str;
|
||||
}
|
||||
|
||||
if(properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
||||
setUppercase(elem->get<bool>("forceUppercase"));
|
||||
|
||||
if(properties & LINE_SPACING && elem->has("lineSpacing"))
|
||||
setLineSpacing(elem->get<float>("lineSpacing"));
|
||||
|
||||
setFont(Font::getFromTheme(elem, properties, mFont));
|
||||
}
|
||||
|
|
|
@ -3,69 +3,37 @@
|
|||
#define ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
|
||||
|
||||
#include "utils/TimeUtil.h"
|
||||
#include "GuiComponent.h"
|
||||
#include "TextComponent.h"
|
||||
|
||||
class TextCache;
|
||||
class ThemeData;
|
||||
|
||||
// Used to enter or display a specific point in time.
|
||||
class DateTimeComponent : public GuiComponent
|
||||
// Used to display date times.
|
||||
class DateTimeComponent : public TextComponent
|
||||
{
|
||||
public:
|
||||
enum DisplayMode
|
||||
{
|
||||
DISP_DATE,
|
||||
DISP_DATE_TIME,
|
||||
DISP_RELATIVE_TO_NOW
|
||||
};
|
||||
DateTimeComponent(Window* window);
|
||||
DateTimeComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color = 0x000000FF, Alignment align = ALIGN_LEFT,
|
||||
Vector3f pos = Vector3f::Zero(), Vector2f size = Vector2f::Zero(), unsigned int bgcolor = 0x00000000);
|
||||
|
||||
DateTimeComponent(Window* window, DisplayMode dispMode = DISP_DATE);
|
||||
void render(const Transform4x4f& parentTrans) override;
|
||||
|
||||
void setValue(const std::string& val) override;
|
||||
std::string getValue() const override;
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void update(int deltaTime) override;
|
||||
void render(const Transform4x4f& parentTrans) override;
|
||||
void onSizeChanged() override;
|
||||
|
||||
// Set how the point in time will be displayed:
|
||||
// * DISP_DATE - only display the date.
|
||||
// * DISP_DATE_TIME - display both the date and the time on that date.
|
||||
// * DISP_RELATIVE_TO_NOW - intelligently display the point in time relative to right now (e.g. "5 secs ago", "3 minutes ago", "1 day ago". Automatically updates as time marches on.
|
||||
// The initial value is DISP_DATE.
|
||||
void setDisplayMode(DisplayMode mode);
|
||||
|
||||
void setColor(unsigned int color); // Text color.
|
||||
void setFont(std::shared_ptr<Font> font); // Font to display with. Default is Font::get(FONT_SIZE_MEDIUM).
|
||||
void setUppercase(bool uppercase); // Force text to be uppercase when in DISP_RELATIVE_TO_NOW mode.
|
||||
void setFormat(const std::string& format);
|
||||
void setDisplayRelative(bool displayRelative);
|
||||
|
||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
|
||||
|
||||
protected:
|
||||
void onTextChanged() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Font> getFont() const;
|
||||
|
||||
std::string getDisplayString(DisplayMode mode) const;
|
||||
DisplayMode getCurrentDisplayMode() const;
|
||||
|
||||
void updateTextCache();
|
||||
std::string getDisplayString() const;
|
||||
|
||||
Utils::Time::DateTime mTime;
|
||||
Utils::Time::DateTime mTimeBeforeEdit;
|
||||
|
||||
bool mEditing;
|
||||
int mEditIndex;
|
||||
DisplayMode mDisplayMode;
|
||||
|
||||
int mRelativeUpdateAccumulator;
|
||||
|
||||
std::unique_ptr<TextCache> mTextCache;
|
||||
std::vector<Vector4f> mCursorBoxes;
|
||||
|
||||
unsigned int mColor;
|
||||
std::shared_ptr<Font> mFont;
|
||||
bool mUppercase;
|
||||
|
||||
bool mAutoSize;
|
||||
std::string mFormat;
|
||||
bool mDisplayRelative;
|
||||
};
|
||||
|
||||
#endif // ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
|
||||
|
|
336
es-core/src/components/DateTimeEditComponent.cpp
Normal file
336
es-core/src/components/DateTimeEditComponent.cpp
Normal file
|
@ -0,0 +1,336 @@
|
|||
#include "components/DateTimeEditComponent.h"
|
||||
|
||||
#include "resources/Font.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "Renderer.h"
|
||||
|
||||
DateTimeEditComponent::DateTimeEditComponent(Window* window, DisplayMode dispMode) : GuiComponent(window),
|
||||
mEditing(false), mEditIndex(0), mDisplayMode(dispMode), mRelativeUpdateAccumulator(0),
|
||||
mColor(0x777777FF), mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT)), mUppercase(false), mAutoSize(true)
|
||||
{
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::setDisplayMode(DisplayMode mode)
|
||||
{
|
||||
mDisplayMode = mode;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
bool DateTimeEditComponent::input(InputConfig* config, Input input)
|
||||
{
|
||||
if(input.value == 0)
|
||||
return false;
|
||||
|
||||
if(config->isMappedTo("a", input))
|
||||
{
|
||||
if(mDisplayMode != DISP_RELATIVE_TO_NOW) //don't allow editing for relative times
|
||||
mEditing = !mEditing;
|
||||
|
||||
if(mEditing)
|
||||
{
|
||||
//started editing
|
||||
mTimeBeforeEdit = mTime;
|
||||
|
||||
//initialize to now if unset
|
||||
if(mTime.getTime() == Utils::Time::NOT_A_DATE_TIME)
|
||||
{
|
||||
mTime = Utils::Time::now();
|
||||
updateTextCache();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(mEditing)
|
||||
{
|
||||
if(config->isMappedTo("b", input))
|
||||
{
|
||||
mEditing = false;
|
||||
mTime = mTimeBeforeEdit;
|
||||
updateTextCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
int incDir = 0;
|
||||
if(config->isMappedTo("up", input) || config->isMappedTo("pageup", input))
|
||||
incDir = 1;
|
||||
else if(config->isMappedTo("down", input) || config->isMappedTo("pagedown", input))
|
||||
incDir = -1;
|
||||
|
||||
if(incDir != 0)
|
||||
{
|
||||
tm new_tm = mTime;
|
||||
|
||||
if(mEditIndex == 0)
|
||||
{
|
||||
new_tm.tm_mon += incDir;
|
||||
|
||||
if(new_tm.tm_mon > 11)
|
||||
new_tm.tm_mon = 0;
|
||||
else if(new_tm.tm_mon < 0)
|
||||
new_tm.tm_mon = 11;
|
||||
|
||||
}
|
||||
else if(mEditIndex == 1)
|
||||
{
|
||||
const int days_in_month = Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
||||
new_tm.tm_mday += incDir;
|
||||
|
||||
if(new_tm.tm_mday > days_in_month)
|
||||
new_tm.tm_mday = 1;
|
||||
else if(new_tm.tm_mday < 1)
|
||||
new_tm.tm_mday = days_in_month;
|
||||
|
||||
}
|
||||
else if(mEditIndex == 2)
|
||||
{
|
||||
new_tm.tm_year += incDir;
|
||||
|
||||
if(new_tm.tm_year < 0)
|
||||
new_tm.tm_year = 0;
|
||||
}
|
||||
|
||||
//validate day
|
||||
const int days_in_month = Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
||||
if(new_tm.tm_mday > days_in_month)
|
||||
new_tm.tm_mday = days_in_month;
|
||||
|
||||
mTime = new_tm;
|
||||
|
||||
updateTextCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(config->isMappedTo("right", input))
|
||||
{
|
||||
mEditIndex++;
|
||||
if(mEditIndex >= (int)mCursorBoxes.size())
|
||||
mEditIndex--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(config->isMappedTo("left", input))
|
||||
{
|
||||
mEditIndex--;
|
||||
if(mEditIndex < 0)
|
||||
mEditIndex++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return GuiComponent::input(config, input);
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::update(int deltaTime)
|
||||
{
|
||||
if(mDisplayMode == DISP_RELATIVE_TO_NOW)
|
||||
{
|
||||
mRelativeUpdateAccumulator += deltaTime;
|
||||
if(mRelativeUpdateAccumulator > 1000)
|
||||
{
|
||||
mRelativeUpdateAccumulator = 0;
|
||||
updateTextCache();
|
||||
}
|
||||
}
|
||||
|
||||
GuiComponent::update(deltaTime);
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::render(const Transform4x4f& parentTrans)
|
||||
{
|
||||
Transform4x4f trans = parentTrans * getTransform();
|
||||
|
||||
if(mTextCache)
|
||||
{
|
||||
// vertically center
|
||||
Vector3f off(0, (mSize.y() - mTextCache->metrics.size.y()) / 2, 0);
|
||||
trans.translate(off);
|
||||
trans.round();
|
||||
|
||||
Renderer::setMatrix(trans);
|
||||
|
||||
std::shared_ptr<Font> font = getFont();
|
||||
|
||||
mTextCache->setColor((mColor & 0xFFFFFF00) | getOpacity());
|
||||
font->renderTextCache(mTextCache.get());
|
||||
|
||||
if(mEditing)
|
||||
{
|
||||
if(mEditIndex >= 0 && (unsigned int)mEditIndex < mCursorBoxes.size())
|
||||
{
|
||||
Renderer::drawRect((int)mCursorBoxes[mEditIndex][0], (int)mCursorBoxes[mEditIndex][1],
|
||||
(int)mCursorBoxes[mEditIndex][2], (int)mCursorBoxes[mEditIndex][3], 0x00000022);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::setValue(const std::string& val)
|
||||
{
|
||||
mTime = val;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
std::string DateTimeEditComponent::getValue() const
|
||||
{
|
||||
return mTime;
|
||||
}
|
||||
|
||||
DateTimeEditComponent::DisplayMode DateTimeEditComponent::getCurrentDisplayMode() const
|
||||
{
|
||||
/*if(mEditing)
|
||||
{
|
||||
if(mDisplayMode == DISP_RELATIVE_TO_NOW)
|
||||
{
|
||||
//TODO: if time component == 00:00:00, return DISP_DATE, else return DISP_DATE_TIME
|
||||
return DISP_DATE;
|
||||
}
|
||||
}*/
|
||||
|
||||
return mDisplayMode;
|
||||
}
|
||||
|
||||
std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const
|
||||
{
|
||||
std::string fmt;
|
||||
switch(mode)
|
||||
{
|
||||
case DISP_DATE:
|
||||
fmt = "%m/%d/%Y";
|
||||
break;
|
||||
case DISP_DATE_TIME:
|
||||
fmt = "%m/%d/%Y %H:%M:%S";
|
||||
break;
|
||||
case DISP_RELATIVE_TO_NOW:
|
||||
{
|
||||
//relative time
|
||||
if(mTime.getTime() == 0)
|
||||
return "never";
|
||||
|
||||
Utils::Time::DateTime now(Utils::Time::now());
|
||||
Utils::Time::Duration dur(now.getTime() - mTime.getTime());
|
||||
|
||||
char buf[64];
|
||||
|
||||
if(dur.getDays() > 0)
|
||||
sprintf(buf, "%d day%s ago", dur.getDays(), (dur.getDays() > 1) ? "s" : "");
|
||||
else if(dur.getHours() > 0)
|
||||
sprintf(buf, "%d hour%s ago", dur.getHours(), (dur.getHours() > 1) ? "s" : "");
|
||||
else if(dur.getMinutes() > 0)
|
||||
sprintf(buf, "%d minute%s ago", dur.getMinutes(), (dur.getMinutes() > 1) ? "s" : "");
|
||||
else
|
||||
sprintf(buf, "%d second%s ago", dur.getSeconds(), (dur.getSeconds() > 1) ? "s" : "");
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(mTime.getTime() == 0)
|
||||
return "unknown";
|
||||
|
||||
return Utils::Time::timeToString(mTime, fmt);
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> DateTimeEditComponent::getFont() const
|
||||
{
|
||||
if(mFont)
|
||||
return mFont;
|
||||
|
||||
return Font::get(FONT_SIZE_MEDIUM);
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::updateTextCache()
|
||||
{
|
||||
DisplayMode mode = getCurrentDisplayMode();
|
||||
const std::string dispString = mUppercase ? Utils::String::toUpper(getDisplayString(mode)) : getDisplayString(mode);
|
||||
std::shared_ptr<Font> font = getFont();
|
||||
mTextCache = std::unique_ptr<TextCache>(font->buildTextCache(dispString, 0, 0, mColor));
|
||||
|
||||
if(mAutoSize)
|
||||
{
|
||||
mSize = mTextCache->metrics.size;
|
||||
|
||||
mAutoSize = false;
|
||||
if(getParent())
|
||||
getParent()->onSizeChanged();
|
||||
}
|
||||
|
||||
//set up cursor positions
|
||||
mCursorBoxes.clear();
|
||||
|
||||
if(dispString.empty() || mode == DISP_RELATIVE_TO_NOW)
|
||||
return;
|
||||
|
||||
//month
|
||||
Vector2f start(0, 0);
|
||||
Vector2f end = font->sizeText(dispString.substr(0, 2));
|
||||
Vector2f diff = end - start;
|
||||
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
|
||||
|
||||
//day
|
||||
start[0] = font->sizeText(dispString.substr(0, 3)).x();
|
||||
end = font->sizeText(dispString.substr(0, 5));
|
||||
diff = end - start;
|
||||
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
|
||||
|
||||
//year
|
||||
start[0] = font->sizeText(dispString.substr(0, 6)).x();
|
||||
end = font->sizeText(dispString.substr(0, 10));
|
||||
diff = end - start;
|
||||
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
|
||||
|
||||
//if mode == DISP_DATE_TIME do times too but I don't wanna do the logic for editing times because no one will ever use it so screw it
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::setColor(unsigned int color)
|
||||
{
|
||||
mColor = color;
|
||||
if(mTextCache)
|
||||
mTextCache->setColor(color);
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::setFont(std::shared_ptr<Font> font)
|
||||
{
|
||||
mFont = font;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::onSizeChanged()
|
||||
{
|
||||
mAutoSize = false;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::setUppercase(bool uppercase)
|
||||
{
|
||||
mUppercase = uppercase;
|
||||
updateTextCache();
|
||||
}
|
||||
|
||||
void DateTimeEditComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
||||
{
|
||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime");
|
||||
if(!elem)
|
||||
return;
|
||||
|
||||
// We set mAutoSize BEFORE calling GuiComponent::applyTheme because it calls
|
||||
// setSize(), which will call updateTextCache(), which will reset mSize if
|
||||
// mAutoSize == true, ignoring the theme's value.
|
||||
if(properties & ThemeFlags::SIZE)
|
||||
mAutoSize = !elem->has("size");
|
||||
|
||||
GuiComponent::applyTheme(theme, view, element, properties);
|
||||
|
||||
using namespace ThemeFlags;
|
||||
|
||||
if(properties & COLOR && elem->has("color"))
|
||||
setColor(elem->get<unsigned int>("color"));
|
||||
|
||||
if(properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
||||
setUppercase(elem->get<bool>("forceUppercase"));
|
||||
|
||||
setFont(Font::getFromTheme(elem, properties, mFont));
|
||||
}
|
71
es-core/src/components/DateTimeEditComponent.h
Normal file
71
es-core/src/components/DateTimeEditComponent.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
#ifndef ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
|
||||
#define ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
|
||||
|
||||
#include "utils/TimeUtil.h"
|
||||
#include "GuiComponent.h"
|
||||
|
||||
class TextCache;
|
||||
|
||||
// Used to enter or display a specific point in time.
|
||||
class DateTimeEditComponent : public GuiComponent
|
||||
{
|
||||
public:
|
||||
enum DisplayMode
|
||||
{
|
||||
DISP_DATE,
|
||||
DISP_DATE_TIME,
|
||||
DISP_RELATIVE_TO_NOW
|
||||
};
|
||||
|
||||
DateTimeEditComponent(Window* window, DisplayMode dispMode = DISP_DATE);
|
||||
|
||||
void setValue(const std::string& val) override;
|
||||
std::string getValue() const override;
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void update(int deltaTime) override;
|
||||
void render(const Transform4x4f& parentTrans) override;
|
||||
void onSizeChanged() override;
|
||||
|
||||
// Set how the point in time will be displayed:
|
||||
// * DISP_DATE - only display the date.
|
||||
// * DISP_DATE_TIME - display both the date and the time on that date.
|
||||
// * DISP_RELATIVE_TO_NOW - intelligently display the point in time relative to right now (e.g. "5 secs ago", "3 minutes ago", "1 day ago". Automatically updates as time marches on.
|
||||
// The initial value is DISP_DATE.
|
||||
void setDisplayMode(DisplayMode mode);
|
||||
|
||||
void setColor(unsigned int color); // Text color.
|
||||
void setFont(std::shared_ptr<Font> font); // Font to display with. Default is Font::get(FONT_SIZE_MEDIUM).
|
||||
void setUppercase(bool uppercase); // Force text to be uppercase when in DISP_RELATIVE_TO_NOW mode.
|
||||
|
||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Font> getFont() const;
|
||||
|
||||
std::string getDisplayString(DisplayMode mode) const;
|
||||
DisplayMode getCurrentDisplayMode() const;
|
||||
|
||||
void updateTextCache();
|
||||
|
||||
Utils::Time::DateTime mTime;
|
||||
Utils::Time::DateTime mTimeBeforeEdit;
|
||||
|
||||
bool mEditing;
|
||||
int mEditIndex;
|
||||
DisplayMode mDisplayMode;
|
||||
|
||||
int mRelativeUpdateAccumulator;
|
||||
|
||||
std::unique_ptr<TextCache> mTextCache;
|
||||
std::vector<Vector4f> mCursorBoxes;
|
||||
|
||||
unsigned int mColor;
|
||||
std::shared_ptr<Font> mFont;
|
||||
bool mUppercase;
|
||||
|
||||
bool mAutoSize;
|
||||
};
|
||||
|
||||
#endif // ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
|
|
@ -42,10 +42,15 @@ public:
|
|||
|
||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
|
||||
|
||||
protected:
|
||||
virtual void onTextChanged();
|
||||
|
||||
std::string mText;
|
||||
std::shared_ptr<Font> mFont;
|
||||
|
||||
private:
|
||||
void calculateExtent();
|
||||
|
||||
void onTextChanged();
|
||||
void onColorChanged();
|
||||
|
||||
unsigned int mColor;
|
||||
|
@ -54,10 +59,8 @@ private:
|
|||
unsigned char mBgColorOpacity;
|
||||
bool mRenderBackground;
|
||||
|
||||
std::shared_ptr<Font> mFont;
|
||||
bool mUppercase;
|
||||
Vector2i mAutoCalcExtent;
|
||||
std::string mText;
|
||||
std::shared_ptr<TextCache> mTextCache;
|
||||
Alignment mHorizontalAlignment;
|
||||
Alignment mVerticalAlignment;
|
||||
|
|
Loading…
Reference in a new issue