Added menu scroll indicators.

This commit is contained in:
Leon Styhre 2021-10-10 18:15:37 +02:00
parent b3220158cb
commit bef997420d
12 changed files with 421 additions and 25 deletions

View file

@ -509,6 +509,18 @@ void GuiMenu::openUIOptions()
}
});
// Enable menu scroll indicators.
auto scroll_indicators = std::make_shared<SwitchComponent>(mWindow);
scroll_indicators->setState(Settings::getInstance()->getBool("ScrollIndicators"));
s->addWithLabel("ENABLE MENU SCROLL INDICATORS", scroll_indicators);
s->addSaveFunc([scroll_indicators, s] {
if (scroll_indicators->getState() != Settings::getInstance()->getBool("ScrollIndicators")) {
Settings::getInstance()->setBool("ScrollIndicators", scroll_indicators->getState());
s->setNeedsSaving();
s->setInvalidateCachedBackground();
}
});
// Enable the 'Y' button for tagging games as favorites.
auto favorites_add_button = std::make_shared<SwitchComponent>(mWindow);
favorites_add_button->setState(Settings::getInstance()->getBool("FavoritesAddButton"));

View file

@ -43,7 +43,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
std::function<void()> deleteGameFunc)
: GuiComponent{window}
, mBackground{window, ":/graphics/frame.svg"}
, mGrid{window, glm::ivec2{1, 5}}
, mGrid{window, glm::ivec2{3, 6}}
, mScraperParams{scraperParams}
, mMetaDataDecl{mdd}
, mMetaData{md}
@ -58,6 +58,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
mTitle = std::make_shared<TextComponent>(mWindow, "EDIT METADATA", Font::get(FONT_SIZE_LARGE),
0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{3, 2});
// Extract possible subfolders from the path.
std::string folderPath =
@ -82,11 +83,24 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, glm::vec3{}, glm::vec2{}, 0x00000000,
0.05f);
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true);
mGrid.setEntry(mSubtitle, glm::ivec2{0, 1}, false, true);
mGrid.setEntry(mSubtitle, glm::ivec2{0, 2}, false, true, glm::ivec2{3, 1});
mList = std::make_shared<ComponentList>(mWindow);
mGrid.setEntry(mList, glm::ivec2{0, 3}, true, true);
mGrid.setEntry(mList, glm::ivec2{0, 4}, true, true, glm::ivec2{3, 1});
// Set up scroll indicators.
mScrollUp = std::make_shared<ImageComponent>(mWindow);
mScrollDown = std::make_shared<ImageComponent>(mWindow);
mScrollIndicator = std::make_shared<ScrollIndicatorComponent>(mList, mScrollUp, mScrollDown);
mScrollUp->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f);
mScrollUp->setOrigin(0.0f, -0.35f);
mScrollDown->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f);
mScrollDown->setOrigin(0.0f, 0.35f);
mGrid.setEntry(mScrollUp, glm::ivec2{2, 0}, false, false, glm::ivec2{1, 1});
mGrid.setEntry(mScrollDown, glm::ivec2{2, 1}, false, false, glm::ivec2{1, 1});
// Populate list.
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) {
@ -463,7 +477,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
}
mButtons = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mButtons, glm::ivec2{0, 4}, true, false);
mGrid.setEntry(mButtons, glm::ivec2{0, 5}, true, false, glm::ivec2{3, 1});
// Resize + center.
float width =
@ -481,10 +495,14 @@ void GuiMetaDataEd::onSizeChanged()
{
const float titleSubtitleSpacing = mSize.y * 0.03f;
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y);
mGrid.setRowHeightPerc(1, titleSubtitleSpacing / mSize.y);
mGrid.setRowHeightPerc(2, (titleSubtitleSpacing * 1.2f) / mSize.y);
mGrid.setRowHeightPerc(3, ((mList->getRowHeight(0) * 10.0f) + 2.0f) / mSize.y);
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y / 2.0f);
mGrid.setRowHeightPerc(1, TITLE_HEIGHT / mSize.y / 2.0f);
mGrid.setRowHeightPerc(2, titleSubtitleSpacing / mSize.y);
mGrid.setRowHeightPerc(3, (titleSubtitleSpacing * 1.2f) / mSize.y);
mGrid.setRowHeightPerc(4, ((mList->getRowHeight(0) * 10.0f) + 2.0f) / mSize.y);
mGrid.setColWidthPerc(0, 0.08f);
mGrid.setColWidthPerc(2, 0.08f);
mGrid.setSize(mSize);
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});

View file

@ -15,6 +15,7 @@
#include "MetaData.h"
#include "components/ComponentGrid.h"
#include "components/NinePatchComponent.h"
#include "components/ScrollIndicatorComponent.h"
#include "guis/GuiSettings.h"
#include "scrapers/Scraper.h"
@ -49,6 +50,9 @@ private:
ComponentGrid mGrid;
std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<ImageComponent> mScrollUp;
std::shared_ptr<ImageComponent> mScrollDown;
std::shared_ptr<ScrollIndicatorComponent> mScrollIndicator;
std::shared_ptr<TextComponent> mSubtitle;
std::shared_ptr<ComponentGrid> mHeaderGrid;
std::shared_ptr<ComponentList> mList;

View file

@ -50,6 +50,7 @@ set(CORE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/components/OptionListComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/RatingComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollableContainer.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollIndicatorComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.h

View file

@ -181,6 +181,7 @@ void Settings::setDefaults()
mBoolMap["SpecialCharsASCII"] = {false, false};
mBoolMap["ListScrollOverlay"] = {false, false};
mBoolMap["VirtualKeyboard"] = {true, true};
mBoolMap["ScrollIndicators"] = {true, true};
mBoolMap["FavoritesAddButton"] = {true, true};
mBoolMap["RandomAddButton"] = {false, false};
mBoolMap["GamelistFilters"] = {true, true};

View file

@ -11,17 +11,19 @@
#define TOTAL_HORIZONTAL_PADDING_PX 20.0f
ComponentList::ComponentList(Window* window)
: IList<ComponentListRow, void*>(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP)
: IList<ComponentListRow, void*>{window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP}
, mFocused{false}
, mSetupCompleted{false}
, mBottomCameraOffset{false}
, mSelectorBarOffset{0.0f}
, mCameraOffset{0.0f}
, mScrollIndicatorStatus{SCROLL_NONE}
{
// Adjust the padding relative to the aspect ratio and screen resolution to make it look
// coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float aspectValue{1.778f / Renderer::getScreenAspectRatio()};
mHorizontalPadding =
TOTAL_HORIZONTAL_PADDING_PX * aspectValue * Renderer::getScreenWidthModifier();
mSelectorBarOffset = 0.0f;
mCameraOffset = 0.0f;
mFocused = false;
}
void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere)
@ -113,6 +115,35 @@ bool ComponentList::input(InputConfig* config, Input input)
void ComponentList::update(int deltaTime)
{
const float totalHeight = getTotalRowHeight();
// Scroll indicator logic, used by ScrollIndicatorComponent.
bool scrollIndicatorChanged = false;
if (totalHeight > mSize.y) {
if (mCameraOffset == 0) {
if (mScrollIndicatorStatus != SCROLL_DOWN) {
mScrollIndicatorStatus = SCROLL_DOWN;
scrollIndicatorChanged = true;
}
}
else if (mBottomCameraOffset) {
if (mScrollIndicatorStatus != SCROLL_UP) {
mScrollIndicatorStatus = SCROLL_UP;
scrollIndicatorChanged = true;
}
}
else if (mCameraOffset > 0) {
if (mScrollIndicatorStatus != SCROLL_UP_DOWN) {
mScrollIndicatorStatus = SCROLL_UP_DOWN;
scrollIndicatorChanged = true;
}
}
}
if (scrollIndicatorChanged == true && mScrollIndicatorChangedCallback != nullptr)
mScrollIndicatorChangedCallback(mScrollIndicatorStatus);
listUpdate(deltaTime);
if (size()) {
@ -125,6 +156,8 @@ void ComponentList::update(int deltaTime)
void ComponentList::onCursorChanged(const CursorState& state)
{
mSetupCompleted = true;
// Update the selector bar position.
// In the future this might be animated.
mSelectorBarOffset = 0;
@ -149,6 +182,8 @@ void ComponentList::onCursorChanged(const CursorState& state)
void ComponentList::updateCameraOffset()
{
float oldCameraOffset = mCameraOffset;
// Move the camera to scroll.
const float totalHeight = getTotalRowHeight();
if (totalHeight > mSize.y) {
@ -160,11 +195,17 @@ void ComponentList::updateCameraOffset()
unsigned int i = 0;
while (mCameraOffset < target && i < mEntries.size()) {
mCameraOffset += getRowHeight(mEntries.at(i).data);
if (mCameraOffset > totalHeight - mSize.y)
if (mCameraOffset > totalHeight - mSize.y) {
if (mSetupCompleted && mCameraOffset != oldCameraOffset)
mBottomCameraOffset = true;
break;
}
i++;
}
if (mCameraOffset < oldCameraOffset)
mBottomCameraOffset = false;
if (mCameraOffset < 0.0f)
mCameraOffset = 0.0f;
}

View file

@ -61,6 +61,13 @@ class ComponentList : public IList<ComponentListRow, void*>
public:
ComponentList(Window* window);
enum ScrollIndicator {
SCROLL_NONE, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
SCROLL_UP,
SCROLL_UP_DOWN,
SCROLL_DOWN
};
void addRow(const ComponentListRow& row, bool setCursorHere = false);
void textInput(const std::string& text) override;
@ -87,12 +94,19 @@ public:
{
return mCursorChangedCallback;
}
void setScrollIndicatorChangedCallback(
const std::function<void(ScrollIndicator state)>& callback)
{
mScrollIndicatorChangedCallback = callback;
}
protected:
void onCursorChanged(const CursorState& state) override;
private:
bool mFocused;
bool mSetupCompleted;
bool mBottomCameraOffset;
void updateCameraOffset();
void updateElementPosition(const ComponentListRow& row);
@ -105,6 +119,9 @@ private:
float mCameraOffset;
std::function<void(CursorState state)> mCursorChangedCallback;
std::function<void(ScrollIndicator state)> mScrollIndicatorChangedCallback;
ScrollIndicator mScrollIndicatorStatus;
};
#endif // ES_CORE_COMPONENTS_COMPONENT_LIST_H

View file

@ -14,14 +14,14 @@
#define BUTTON_GRID_VERT_PADDING 32.0f
#define BUTTON_GRID_HORIZ_PADDING 10.0f
#define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + TITLE_VERT_PADDING)
#define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + Renderer::getScreenHeight() * 0.0637f)
MenuComponent::MenuComponent(Window* window,
std::string title,
const std::shared_ptr<Font>& titleFont)
: GuiComponent(window)
, mBackground(window)
, mGrid(window, glm::ivec2{1, 3})
, mGrid(window, glm::ivec2{3, 4})
, mNeedsSaving(false)
{
addChild(&mBackground);
@ -34,11 +34,25 @@ MenuComponent::MenuComponent(Window* window,
mTitle->setHorizontalAlignment(ALIGN_CENTER);
mTitle->setColor(0x555555FF);
setTitle(title, titleFont);
mGrid.setEntry(mTitle, glm::ivec2{}, false);
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{3, 2});
// Set up list which will never change (externally, anyway).
mList = std::make_shared<ComponentList>(mWindow);
mGrid.setEntry(mList, glm::ivec2{0, 1}, true);
mGrid.setEntry(mList, glm::ivec2{0, 2}, true, true, glm::ivec2{3, 1});
// Set up scroll indicators.
mScrollUp = std::make_shared<ImageComponent>(mWindow);
mScrollDown = std::make_shared<ImageComponent>(mWindow);
mScrollIndicator = std::make_shared<ScrollIndicatorComponent>(mList, mScrollUp, mScrollDown);
mScrollUp->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f);
mScrollUp->setOrigin(0.0f, -0.35f);
mScrollDown->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f);
mScrollDown->setOrigin(0.0f, 0.35f);
mGrid.setEntry(mScrollUp, glm::ivec2{2, 0}, false, false, glm::ivec2{1, 1});
mGrid.setEntry(mScrollDown, glm::ivec2{2, 1}, false, false, glm::ivec2{1, 1});
updateGrid();
updateSize();
@ -109,8 +123,12 @@ void MenuComponent::onSizeChanged()
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
// Update grid row/column sizes.
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y);
mGrid.setRowHeightPerc(2, getButtonGridHeight() / mSize.y);
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y / 2.0f);
mGrid.setRowHeightPerc(1, TITLE_HEIGHT / mSize.y / 2.0f);
mGrid.setRowHeightPerc(3, getButtonGridHeight() / mSize.y);
mGrid.setColWidthPerc(0, 0.08f);
mGrid.setColWidthPerc(2, 0.08f);
mGrid.setSize(mSize);
}
@ -134,7 +152,7 @@ void MenuComponent::updateGrid()
if (mButtons.size()) {
mButtonGrid = makeButtonGrid(mWindow, mButtons);
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 2}, true, false);
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 3}, true, false, glm::ivec2{3, 1});
}
}

View file

@ -12,13 +12,12 @@
#include "components/ComponentGrid.h"
#include "components/ComponentList.h"
#include "components/NinePatchComponent.h"
#include "components/ScrollIndicatorComponent.h"
#include "components/TextComponent.h"
#include "utils/StringUtil.h"
#include <cmath>
#define TITLE_VERT_PADDING (Renderer::getScreenHeight() * 0.0637f)
class ButtonComponent;
class ImageComponent;
@ -87,6 +86,9 @@ private:
ComponentGrid mGrid;
std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<ImageComponent> mScrollUp;
std::shared_ptr<ImageComponent> mScrollDown;
std::shared_ptr<ScrollIndicatorComponent> mScrollIndicator;
std::shared_ptr<ComponentList> mList;
std::shared_ptr<ComponentGrid> mButtonGrid;
std::vector<std::shared_ptr<ButtonComponent>> mButtons;

View file

@ -0,0 +1,134 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// ScrollIndicatorComponent.h
//
// Visually indicates whether a menu can be scrolled (up, up/down or down).
//
#ifndef ES_CORE_COMPONENTS_SCROLL_INDICATOR_COMPONENT_H
#define ES_CORE_COMPONENTS_SCROLL_INDICATOR_COMPONENT_H
#define FADE_IN_TIME 90.0f
#include "animations/LambdaAnimation.h"
#include "components/ComponentList.h"
class ScrollIndicatorComponent
{
public:
ScrollIndicatorComponent(std::shared_ptr<ComponentList> componentList,
std::shared_ptr<ImageComponent> scrollUp,
std::shared_ptr<ImageComponent> scrollDown)
: mPreviousScrollState(ComponentList::SCROLL_NONE)
{
assert(componentList != nullptr && scrollUp != nullptr && scrollDown != nullptr);
scrollUp->setImage(":/graphics/scroll_up.svg");
scrollDown->setImage(":/graphics/scroll_down.svg");
scrollUp->setOpacity(0);
scrollDown->setOpacity(0);
if (!Settings::getInstance()->getBool("ScrollIndicators"))
return;
componentList.get()->setScrollIndicatorChangedCallback(
[this, scrollUp, scrollDown](ComponentList::ScrollIndicator state) {
float fadeInTime{FADE_IN_TIME};
bool upFadeIn = false;
bool upFadeOut = false;
bool downFadeIn = false;
bool downFadeOut = false;
scrollUp->finishAnimation(0);
scrollDown->finishAnimation(0);
if (state == ComponentList::SCROLL_UP &&
mPreviousScrollState == ComponentList::SCROLL_NONE) {
scrollUp->setOpacity(255);
}
else if (state == ComponentList::SCROLL_UP &&
mPreviousScrollState == ComponentList::SCROLL_UP_DOWN) {
downFadeOut = true;
}
else if (state == ComponentList::SCROLL_UP &&
mPreviousScrollState == ComponentList::SCROLL_DOWN) {
upFadeIn = true;
fadeInTime *= 1.5f;
scrollDown->setOpacity(0);
}
else if (state == ComponentList::SCROLL_UP_DOWN &&
mPreviousScrollState == ComponentList::SCROLL_NONE) {
scrollUp->setOpacity(255);
scrollDown->setOpacity(255);
}
else if (state == ComponentList::SCROLL_UP_DOWN &&
mPreviousScrollState == ComponentList::SCROLL_DOWN) {
upFadeIn = true;
}
else if (state == ComponentList::SCROLL_UP_DOWN &&
mPreviousScrollState == ComponentList::SCROLL_UP) {
downFadeIn = true;
}
else if (state == ComponentList::SCROLL_DOWN &&
mPreviousScrollState == ComponentList::SCROLL_NONE) {
scrollDown->setOpacity(255);
}
else if (state == ComponentList::SCROLL_DOWN &&
mPreviousScrollState == ComponentList::SCROLL_UP_DOWN) {
upFadeOut = true;
}
else if (state == ComponentList::SCROLL_DOWN &&
mPreviousScrollState == ComponentList::SCROLL_UP) {
downFadeIn = true;
fadeInTime *= 1.5f;
scrollUp->setOpacity(0);
}
if (downFadeIn) {
auto downFadeInFunc = [scrollDown](float t) {
scrollDown->setOpacity(
static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
scrollDown->setAnimation(new LambdaAnimation(downFadeInFunc, fadeInTime), 0,
nullptr, false);
}
if (downFadeOut) {
auto downFadeOutFunc = [scrollDown](float t) {
scrollDown->setOpacity(
static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
scrollDown->setAnimation(new LambdaAnimation(downFadeOutFunc, fadeInTime), 0,
nullptr, true);
}
if (upFadeIn) {
auto upFadeInFunc = [scrollUp](float t) {
scrollUp->setOpacity(
static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
scrollUp->setAnimation(new LambdaAnimation(upFadeInFunc, fadeInTime), 0,
nullptr, false);
}
if (upFadeOut) {
auto upFadeOutFunc = [scrollUp](float t) {
scrollUp->setOpacity(
static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
scrollUp->setAnimation(new LambdaAnimation(upFadeOutFunc, fadeInTime), 0,
nullptr, true);
}
mPreviousScrollState = state;
});
}
private:
ComponentList::ScrollIndicator mPreviousScrollState;
};
#endif // ES_CORE_COMPONENTS_SCROLL_INDICATOR_COMPONENT_H

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="28"
height="28"
version="1.1"
viewBox="0 0 28 28"
id="svg4"
sodipodi:docname="scroll_down.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 9 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="34 : 9 : 1"
inkscape:persp3d-origin="17 : 6 : 1"
id="perspective4894" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2065"
id="namedview6"
showgrid="false"
inkscape:zoom="32.768734"
inkscape:cx="4.456952"
inkscape:cy="10.696616"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
inkscape:snap-global="false" />
<path
d="M 25.554061,13.731199 14.000019,25.828097 2.4459392,13.731199"
id="path2-6-9-4-0-3"
inkscape:connector-curvature="0"
style="fill:none;stroke:#878787;stroke-width:4.34399986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:transform-center-x="-0.017545518"
inkscape:transform-center-y="1.1060305" />
<path
d="M 25.554061,2.1944304 14.000021,14.291328 2.4459392,2.1944304"
id="path2-6-9-4-0-3-0"
inkscape:connector-curvature="0"
style="fill:none;stroke:#878787;stroke-width:4.34399986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:transform-center-x="-0.017544818"
inkscape:transform-center-y="1.1060303" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="28"
height="28"
version="1.1"
viewBox="0 0 28 28"
id="svg4"
sodipodi:docname="scroll_up.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 9 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="34 : 9 : 1"
inkscape:persp3d-origin="17 : 6 : 1"
id="perspective4894" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2065"
id="namedview6"
showgrid="false"
inkscape:zoom="46.341988"
inkscape:cx="15.475362"
inkscape:cy="12.000926"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
inkscape:snap-global="false" />
<path
d="M 2.4459395,14.268801 13.999981,2.1719027 25.55406,14.268801"
id="path2-6-9-4-0-3"
inkscape:connector-curvature="0"
style="fill:none;stroke:#878787;stroke-width:4.34399986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:transform-center-x="0.017545382"
inkscape:transform-center-y="-1.1060306" />
<path
d="M 2.4459391,25.80557 13.99998,13.708672 25.554061,25.80557"
id="path2-6-9-4-0-3-0"
inkscape:connector-curvature="0"
style="fill:none;stroke:#878787;stroke-width:4.34399986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:transform-center-x="0.017545032"
inkscape:transform-center-y="-1.1060303" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB