423 lines
10 KiB
C++
423 lines
10 KiB
C++
/*
|
|
Borealis, a Nintendo Switch UI Library
|
|
Copyright (C) 2019-2020 natinusala
|
|
Copyright (C) 2019 p-sam
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <libretro-common/features/features_cpu.h>
|
|
#include <stdio.h>
|
|
|
|
#include <borealis/actions.hpp>
|
|
#include <borealis/event.hpp>
|
|
#include <borealis/frame_context.hpp>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace brls
|
|
{
|
|
|
|
// The animation to play when
|
|
// pushing or popping a view
|
|
// (implemented in Application)
|
|
enum class ViewAnimation
|
|
{
|
|
FADE, // the old view fades away and the new one fades in
|
|
SLIDE_LEFT, // the old view slides out to the left and the new one slides in from the right
|
|
SLIDE_RIGHT // inverted SLIDE_LEFT
|
|
};
|
|
|
|
// Focus direction when navigating
|
|
enum class FocusDirection
|
|
{
|
|
UP,
|
|
DOWN,
|
|
LEFT,
|
|
RIGHT
|
|
};
|
|
|
|
// View background
|
|
enum class ViewBackground
|
|
{
|
|
NONE,
|
|
SIDEBAR,
|
|
DEBUG,
|
|
BACKDROP
|
|
};
|
|
|
|
enum class ViewType
|
|
{
|
|
VIEW,
|
|
SIDEBARITEM,
|
|
};
|
|
|
|
extern NVGcolor transparent;
|
|
|
|
class View;
|
|
|
|
typedef Event<View*> GenericEvent;
|
|
typedef Event<> VoidEvent;
|
|
|
|
// Superclass for all the other views
|
|
// Lifecycle of a view is :
|
|
// new -> [willAppear -> willDisappear] -> delete
|
|
//
|
|
// Users have do to the new, the rest of the lifecycle is taken
|
|
// care of by the library
|
|
//
|
|
// willAppear and willDisappear can be called zero or multiple times
|
|
// before deletion (in case of a TabLayout for instance)
|
|
class View
|
|
{
|
|
private:
|
|
ViewBackground background = ViewBackground::NONE;
|
|
|
|
void drawBackground(NVGcontext* vg, FrameContext* ctx, Style* style);
|
|
void drawHighlight(NVGcontext* vg, Theme* theme, float alpha, Style* style, bool background);
|
|
|
|
float highlightAlpha = 0.0f;
|
|
float clickAnimationAlpha = 0.0f;
|
|
|
|
bool dirty = true;
|
|
|
|
bool highlightShaking = false;
|
|
retro_time_t highlightShakeStart;
|
|
FocusDirection highlightShakeDirection;
|
|
float highlightShakeAmplitude;
|
|
|
|
bool fadeIn = false; // is the fade in animation running?
|
|
bool forceTranslucent = false;
|
|
|
|
Theme* themeOverride = nullptr;
|
|
|
|
bool hidden = false;
|
|
|
|
std::vector<Action> actions;
|
|
|
|
ViewType viewType = ViewType::VIEW;
|
|
|
|
/**
|
|
* Parent user data, typically the index of the view
|
|
* in the internal layout structure
|
|
*/
|
|
void* parentUserdata = nullptr;
|
|
|
|
protected:
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
unsigned width = 0;
|
|
unsigned height = 0;
|
|
|
|
float collapseState = 1.0f;
|
|
|
|
bool focused = false;
|
|
|
|
View* parent = nullptr;
|
|
|
|
GenericEvent focusEvent;
|
|
|
|
virtual unsigned getShowAnimationDuration(ViewAnimation animation);
|
|
|
|
virtual void getHighlightInsets(unsigned* top, unsigned* right, unsigned* bottom, unsigned* left)
|
|
{
|
|
*top = 0;
|
|
*right = 0;
|
|
*bottom = 0;
|
|
*left = 0;
|
|
}
|
|
|
|
virtual void getHighlightMetrics(Style* style, float* cornerRadius)
|
|
{
|
|
*cornerRadius = style->Highlight.cornerRadius;
|
|
}
|
|
|
|
virtual bool isHighlightBackgroundEnabled()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Helper functions to apply this view's alpha to a color
|
|
NVGcolor a(NVGcolor color);
|
|
NVGpaint a(NVGpaint paint);
|
|
|
|
NVGcolor RGB(unsigned r, unsigned g, unsigned b)
|
|
{
|
|
return this->a(nvgRGB(r, g, b));
|
|
}
|
|
|
|
NVGcolor RGBA(unsigned r, unsigned g, unsigned b, unsigned a)
|
|
{
|
|
return this->a(nvgRGBA(r, g, b, a));
|
|
}
|
|
|
|
NVGcolor RGBf(float r, float g, float b)
|
|
{
|
|
return this->a(nvgRGBf(r, g, b));
|
|
}
|
|
|
|
NVGcolor RGBAf(float r, float g, float b, float a)
|
|
{
|
|
return this->a(nvgRGBAf(r, g, b, a));
|
|
}
|
|
|
|
/**
|
|
* Should the hint alpha be animated when
|
|
* pushing the view?
|
|
*/
|
|
virtual bool animateHint()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
void setBoundaries(int x, int y, unsigned width, unsigned height);
|
|
|
|
void setBackground(ViewBackground background);
|
|
|
|
void setWidth(unsigned width);
|
|
void setHeight(unsigned height);
|
|
|
|
void shakeHighlight(FocusDirection direction);
|
|
|
|
int getX();
|
|
int getY();
|
|
unsigned getWidth();
|
|
unsigned getHeight(bool includeCollapse = true);
|
|
|
|
void setForceTranslucent(bool translucent);
|
|
|
|
void setParent(View* parent, void* parentUserdata = nullptr);
|
|
View* getParent();
|
|
bool hasParent();
|
|
|
|
void* getParentUserData();
|
|
|
|
void registerAction(std::string hintText, Key key, ActionListener actionListener, bool hidden = false);
|
|
void updateActionHint(Key key, std::string hintText);
|
|
void setActionAvailable(Key key, bool available);
|
|
|
|
virtual void playClickAnimation();
|
|
|
|
std::string describe() const { return typeid(*this).name(); }
|
|
|
|
const std::vector<Action>& getActions()
|
|
{
|
|
return this->actions;
|
|
}
|
|
|
|
virtual ViewType getViewType()
|
|
{
|
|
return this->viewType;
|
|
}
|
|
|
|
/**
|
|
* Called each frame
|
|
* Do not override it to draw your view,
|
|
* override draw() instead
|
|
*/
|
|
virtual void frame(FrameContext* ctx);
|
|
|
|
/**
|
|
* Called by frame() to draw
|
|
* the view onscreen
|
|
*/
|
|
virtual void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) = 0;
|
|
|
|
/**
|
|
* Triggered when the view has been
|
|
* resized and needs to layout its
|
|
* children
|
|
*/
|
|
virtual void layout(NVGcontext* vg, Style* style, FontStash* stash)
|
|
{
|
|
// Nothing to do
|
|
}
|
|
|
|
/**
|
|
* Called when the view will appear
|
|
* on screen, before or after layout()
|
|
*
|
|
* Can be called if the view has
|
|
* already appeared, so be careful
|
|
*/
|
|
virtual void willAppear(bool resetState = false)
|
|
{
|
|
// Nothing to do
|
|
}
|
|
|
|
/**
|
|
* Called when the view will disappear
|
|
* from the screen
|
|
*
|
|
* Can be called if the view has
|
|
* already disappeared, so be careful
|
|
*/
|
|
virtual void willDisappear(bool resetState = false)
|
|
{
|
|
// Nothing to do
|
|
}
|
|
|
|
/**
|
|
* Called when the show() animation (fade in)
|
|
* ends
|
|
*/
|
|
virtual void onShowAnimationEnd() {};
|
|
|
|
/**
|
|
* Shows the view (fade in animation)
|
|
*
|
|
* Called once when the view is
|
|
* pushed to the view stack
|
|
*
|
|
* Not recursive
|
|
*/
|
|
virtual void show(std::function<void(void)> cb, bool animate = true, ViewAnimation animation = ViewAnimation::FADE);
|
|
|
|
/**
|
|
* Hides the view in a collapse animation
|
|
*/
|
|
void collapse(bool animated = true);
|
|
|
|
bool isCollapsed();
|
|
|
|
/**
|
|
* Shows the view in a expand animation (opposite
|
|
* of collapse)
|
|
*/
|
|
void expand(bool animated = true);
|
|
|
|
/**
|
|
* Hides the view (fade out animation)
|
|
*
|
|
* Called if another view is pushed
|
|
* on top of this one
|
|
*
|
|
* Not recursive
|
|
*/
|
|
virtual void hide(std::function<void(void)> cb, bool animated = true, ViewAnimation animation = ViewAnimation::FADE);
|
|
|
|
bool isHidden();
|
|
|
|
/**
|
|
* Calls layout() on next frame
|
|
* unless immediate is true in which case
|
|
* it's called immediately
|
|
*/
|
|
void invalidate(bool immediate = false);
|
|
|
|
/**
|
|
* Is this view translucent?
|
|
*
|
|
* If you override it please return
|
|
* <value> || View::isTranslucent()
|
|
* to keep the fadeIn transition
|
|
*/
|
|
virtual bool isTranslucent()
|
|
{
|
|
return fadeIn || forceTranslucent;
|
|
}
|
|
|
|
bool isFocused();
|
|
|
|
/**
|
|
* Returns the default view to focus when focusing this view
|
|
* Typically the view itself or one of its children
|
|
*
|
|
* Returning nullptr means that the view is not focusable
|
|
* (and neither are its children)
|
|
*
|
|
* When pressing a key, the flow is :
|
|
* 1. starting from the currently focused view's parent, traverse the tree upwards and
|
|
* repeatidly call getNextFocus() on every view until we find a next view to focus or meet the end of the tree
|
|
* 2. if a view is found, getNextFocus() will internally call getDefaultFocus() for the selected child
|
|
* 3. give focus to the result, if it exists
|
|
*/
|
|
virtual View* getDefaultFocus()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Returns the next view to focus given the requested direction
|
|
* and the currently focused view (as parent user data)
|
|
*
|
|
* Returning nullptr means that there is no next view to focus
|
|
* in that direction - getNextFocus will then be called on our
|
|
* parent if any
|
|
*/
|
|
virtual View* getNextFocus(FocusDirection direction, View* currentView)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Fired when focus is gained
|
|
*/
|
|
virtual void onFocusGained();
|
|
|
|
/**
|
|
* Fired when focus is lost
|
|
*/
|
|
virtual void onFocusLost();
|
|
|
|
/**
|
|
* Fired when focus is gained on one of this view's children
|
|
*/
|
|
virtual void onChildFocusGained(View* child)
|
|
{
|
|
if (this->hasParent())
|
|
this->getParent()->onChildFocusGained(this);
|
|
}
|
|
|
|
/**
|
|
* Fired when focus is gained on one of this view's children
|
|
*/
|
|
virtual void onChildFocusLost(View* child)
|
|
{
|
|
if (this->hasParent())
|
|
this->getParent()->onChildFocusLost(this);
|
|
}
|
|
|
|
/**
|
|
* Fired when the window size changes
|
|
* Not guaranteed to be called before or after layout()
|
|
*/
|
|
virtual void onWindowSizeChanged()
|
|
{
|
|
// Nothing by default
|
|
}
|
|
|
|
GenericEvent* getFocusEvent();
|
|
|
|
float alpha = 1.0f;
|
|
|
|
virtual float getAlpha(bool child = false);
|
|
|
|
/**
|
|
* Forces this view and its children to use
|
|
* the specified theme variant
|
|
*/
|
|
void overrideThemeVariant(Theme* newTheme);
|
|
|
|
virtual ~View();
|
|
};
|
|
|
|
} // namespace brls
|