[home] Added support for a wallpaper in a special format (.obm)

This commit is contained in:
Laury
2021-07-06 14:54:55 +02:00
committed by M4x1m3
parent c3b752d6cc
commit 0366c4cd00
15 changed files with 291 additions and 30 deletions

View File

@@ -3,6 +3,7 @@ app_home_src = $(addprefix apps/home/,\
app_cell.cpp \
apps_layout.py \
controller.cpp \
selectable_table_view_with_background.cpp \
)
apps_src += $(app_home_src)

View File

@@ -8,6 +8,7 @@ namespace Home {
AppCell::AppCell() :
HighlightCell(),
m_nameView(KDFont::SmallFont, (I18n::Message)0, 0.5f, 0.5f, Palette::HomeCellText, Palette::HomeCellBackground),
m_backgroundView(nullptr),
m_visible(true), m_external_app(false)
{
}
@@ -15,8 +16,8 @@ AppCell::AppCell() :
void AppCell::drawRect(KDContext * ctx, KDRect rect) const {
KDSize nameSize = m_nameView.minimalSizeForOptimalDisplay();
ctx->fillRect(KDRect(0, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, bounds().width(), nameSize.height()+2*k_nameHeightMargin), Palette::HomeBackground);
}
m_backgroundView->drawRect(ctx, KDRect(0, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, bounds().width(), nameSize.height()+2*k_nameHeightMargin));
}
int AppCell::numberOfSubviews() const {
return m_visible ? 2 : 0;
@@ -70,6 +71,10 @@ void AppCell::setVisible(bool visible) {
}
}
void AppCell::setBackgroundView(const BackgroundView * backgroundView) {
m_backgroundView = backgroundView;
}
void AppCell::reloadCell() {
m_nameView.setTextColor(isHighlighted() ? (m_external_app ? Palette::HomeCellTextExternalActive : Palette::HomeCellTextActive) : (m_external_app ? Palette::HomeCellTextExternal : Palette::HomeCellText));
m_nameView.setBackgroundColor(isHighlighted() ? Palette::HomeCellBackgroundActive : Palette::HomeCellBackground);

View File

@@ -15,6 +15,7 @@ public:
void layoutSubviews(bool force = false) override;
void setVisible(bool visible);
void setBackgroundView(const BackgroundView * backgroundView);
void reloadCell() override;
void setAppDescriptor(::App::Descriptor * appDescriptor);
void setExtAppDescriptor(const char* name, const Image* icon);
@@ -25,8 +26,9 @@ private:
static constexpr KDCoordinate k_iconHeight = 56;
static constexpr KDCoordinate k_nameWidthMargin = 4;
static constexpr KDCoordinate k_nameHeightMargin = 1;
ImageView m_iconView;
IconView m_iconView;
MessageTextView m_nameView;
const BackgroundView * m_backgroundView;
bool m_visible;
bool m_external_app;
};

View File

@@ -18,11 +18,11 @@ extern "C" {
namespace Home {
Controller::ContentView::ContentView(Controller * controller, SelectableTableViewDataSource * selectionDataSource) :
m_selectableTableView(controller, controller, selectionDataSource, controller)
m_selectableTableView(controller, controller, &m_backgroundView, selectionDataSource, controller),
m_backgroundView()
{
m_selectableTableView.setVerticalCellOverlap(0);
m_selectableTableView.setMargins(0, k_sideMargin, k_bottomMargin, k_sideMargin);
m_selectableTableView.setBackgroundColor(Palette::HomeBackground);
static_cast<ScrollView::BarDecorator *>(m_selectableTableView.decorator())->verticalBar()->setMargin(k_indicatorMargin);
}
@@ -31,7 +31,7 @@ SelectableTableView * Controller::ContentView::selectableTableView() {
}
void Controller::ContentView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(bounds(), Palette::HomeBackground);
m_selectableTableView.drawRect(ctx, rect);
}
void Controller::ContentView::reloadBottomRow(SimpleTableViewDataSource * dataSource, int numberOfIcons, int numberOfColumns) {
@@ -45,6 +45,10 @@ void Controller::ContentView::reloadBottomRow(SimpleTableViewDataSource * dataSo
}
}
BackgroundView * Controller::ContentView::backgroundView() {
return &m_backgroundView;
}
int Controller::ContentView::numberOfSubviews() const {
return 1;
}
@@ -56,6 +60,8 @@ View * Controller::ContentView::subviewAtIndex(int index) {
void Controller::ContentView::layoutSubviews(bool force) {
m_selectableTableView.setFrame(bounds(), force);
m_backgroundView.setFrame(KDRect(0, Metric::TitleBarHeight, Ion::Display::Width, Ion::Display::Height-Metric::TitleBarHeight), force);
m_backgroundView.updateDataValidity();
}
Controller::Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource, ::App * app) :
@@ -63,6 +69,21 @@ Controller::Controller(Responder * parentResponder, SelectableTableViewDataSourc
m_view(this, selectionDataSource)
{
m_app = app;
for (int i = 0; i < k_maxNumberOfCells; i++) {
m_cells[i].setBackgroundView(m_view.backgroundView());
}
m_view.backgroundView()->setDefaultColor(Palette::HomeBackground);
#ifdef HOME_DISPLAY_EXTERNALS
int index = External::Archive::indexFromName("wallpaper.obm");
if(index > -1) {
External::Archive::File image;
External::Archive::fileAtIndex(index, image);
m_view.backgroundView()->setBackgroundImage(image.data);
}
#endif
}
bool Controller::handleEvent(Ion::Events::Event event) {

View File

@@ -2,6 +2,7 @@
#define HOME_CONTROLLER_H
#include <escher.h>
#include "selectable_table_view_with_background.h"
#include "app_cell.h"
namespace Home {
@@ -35,11 +36,13 @@ private:
SelectableTableView * selectableTableView();
void drawRect(KDContext * ctx, KDRect rect) const override;
void reloadBottomRow(SimpleTableViewDataSource * dataSource, int numberOfIcons, int numberOfColumns);
BackgroundView * backgroundView();
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
SelectableTableView m_selectableTableView;
SelectableTableViewWithBackground m_selectableTableView;
BackgroundView m_backgroundView;
};
static constexpr KDCoordinate k_sideMargin = 4;
static constexpr KDCoordinate k_bottomMargin = 14;

View File

@@ -0,0 +1,16 @@
#include "selectable_table_view_with_background.h"
SelectableTableViewWithBackground::SelectableTableViewWithBackground(Responder * parentResponder, TableViewDataSource * dataSource, BackgroundView * backgroundView, SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate) :
SelectableTableView(parentResponder, dataSource, selectionDataSource, delegate),
m_backgroundInnerView(this, backgroundView)
{
}
void SelectableTableViewWithBackground::BackgroundInnerView::drawRect(KDContext * ctx, KDRect rect) const {
m_backgroundView->drawRect(ctx, rect);
}
void SelectableTableViewWithBackground::BackgroundInnerView::setBackgroundView(const uint8_t * data) {
m_backgroundView->setBackgroundImage(data);
}

View File

@@ -0,0 +1,25 @@
#ifndef ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H
#define ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H
#include <escher/selectable_table_view.h>
#include <escher/background_view.h>
class SelectableTableViewWithBackground : public SelectableTableView {
public:
SelectableTableViewWithBackground(Responder * parentResponder, TableViewDataSource * dataSource, BackgroundView * backgroundView,
SelectableTableViewDataSource * selectionDataSource = nullptr, SelectableTableViewDelegate * delegate = nullptr);
View * subviewAtIndex(int index) override { return (index == 0) ? &m_backgroundInnerView : decorator()->indicatorAtIndex(index); }
protected:
virtual InnerView * getInnerView() { return &m_backgroundInnerView; }
class BackgroundInnerView : public ScrollView::InnerView {
public:
BackgroundInnerView(ScrollView * scrollView, BackgroundView * backgroundView): InnerView(scrollView), m_backgroundView(backgroundView) {}
void drawRect(KDContext * ctx, KDRect rect) const override;
void setBackgroundView(const uint8_t * data);
private:
BackgroundView * m_backgroundView;
};
BackgroundInnerView m_backgroundInnerView;
};
#endif

View File

@@ -22,6 +22,7 @@ endif
escher_src += $(addprefix escher/src/,\
alternate_empty_view_controller.cpp \
app.cpp \
background_view.cpp \
bank_view_controller.cpp \
bordered.cpp \
buffer_text_view.cpp \
@@ -46,6 +47,7 @@ escher_src += $(addprefix escher/src/,\
expression_view.cpp \
highlight_cell.cpp \
gauge_view.cpp \
icon_view.cpp \
image_view.cpp \
input_event_handler.cpp \
invocation.cpp \

View File

@@ -3,6 +3,7 @@
#include <escher/alternate_empty_view_controller.h>
#include <escher/alternate_empty_view_delegate.h>
#include <escher/background_view.h>
#include <escher/bank_view_controller.h>
#include <escher/buffer_text_view.h>
#include <escher/button.h>
@@ -26,6 +27,7 @@
#include <escher/expression_view.h>
#include <escher/gauge_view.h>
#include <escher/highlight_cell.h>
#include <escher/icon_view.h>
#include <escher/image.h>
#include <escher/image_view.h>
#include <escher/input_event_handler.h>

View File

@@ -0,0 +1,19 @@
#ifndef ESCHER_BACKGROUND_VIEW_H
#define ESCHER_BACKGROUND_VIEW_H
#include <escher/view.h>
class BackgroundView : public View {
public:
BackgroundView();
void drawRect(KDContext * ctx, KDRect rect) const override;
void setBackgroundImage(const uint8_t * data);
void setDefaultColor(KDColor defaultColor);
void updateDataValidity();
private:
const uint8_t * m_data;
bool m_isDataValid;
KDColor m_defaultColor;
};
#endif

View File

@@ -0,0 +1,20 @@
#ifndef ESCHER_ICON_VIEW_H
#define ESCHER_ICON_VIEW_H
#include <escher/view.h>
#include <escher/image.h>
class IconView : public View {
//Unlike the ImageView class, IconView displays an image with rounded corners
public:
IconView();
void setImage(const Image * image);
void setImage(const uint8_t *data, size_t dataLength);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
const Image * m_image;
const uint8_t * m_data;
size_t m_dataLength;
};
#endif

View File

@@ -96,25 +96,6 @@ public:
void scrollToContentPoint(KDPoint p, bool allowOverscroll = false);
void scrollToContentRect(KDRect rect, bool allowOverscroll = false); // Minimal scrolling to make this rect visible
protected:
KDCoordinate maxContentWidthDisplayableWithoutScrolling() const {
return m_frame.width() - m_leftMargin - m_rightMargin;
}
KDCoordinate maxContentHeightDisplayableWithoutScrolling() const {
return m_frame.height() - m_topMargin - m_bottomMargin;
}
KDRect visibleContentRect();
void layoutSubviews(bool force = false) override;
virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); }
#if ESCHER_VIEW_LOGGING
const char * className() const override;
void logAttributes(std::ostream &os) const override;
#endif
View * m_contentView;
private:
ScrollViewDataSource * m_dataSource;
int numberOfSubviews() const override { return 1 + const_cast<ScrollView *>(this)->decorator()->numberOfIndicators(); }
View * subviewAtIndex(int index) override { return (index == 0) ? &m_innerView : decorator()->indicatorAtIndex(index); }
class InnerView : public View {
public:
InnerView(ScrollView * scrollView) : View(), m_scrollView(scrollView) {}
@@ -127,13 +108,33 @@ private:
}
const ScrollView * m_scrollView;
};
KDCoordinate maxContentWidthDisplayableWithoutScrolling() const {
return m_frame.width() - m_leftMargin - m_rightMargin;
}
KDCoordinate maxContentHeightDisplayableWithoutScrolling() const {
return m_frame.height() - m_topMargin - m_bottomMargin;
}
KDRect visibleContentRect();
void layoutSubviews(bool force = false) override;
virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); }
virtual InnerView * getInnerView() { return &m_innerView; }
#if ESCHER_VIEW_LOGGING
const char * className() const override;
void logAttributes(std::ostream &os) const override;
#endif
View * m_contentView;
InnerView m_innerView;
private:
ScrollViewDataSource * m_dataSource;
int numberOfSubviews() const override { return 1 + const_cast<ScrollView *>(this)->decorator()->numberOfIndicators(); }
View * subviewAtIndex(int index) override { return (index == 0) ? &m_innerView : decorator()->indicatorAtIndex(index); }
KDCoordinate m_topMargin;
KDCoordinate m_rightMargin;
KDCoordinate m_bottomMargin;
KDCoordinate m_leftMargin;
InnerView m_innerView;
Decorator::Type m_decoratorType;
union Decorators {
public:

View File

@@ -0,0 +1,65 @@
#include <escher/background_view.h>
#include <ion.h>
#include <kandinsky/ion_context.h>
#include <escher/palette.h>//AND THIS
#include "apps/home/controller.h"
#ifdef HOME_DISPLAY_EXTERNALS
#include "apps/external/external_icon.h"
#include "apps/external/archive.h"
#include <string.h>
#endif
//To store the Omega backgrounds, we use a specific file in the "OmegaBitMap" format.
//Here is its header
struct OBMHeader
{
uint32_t signature; //Normally it is 32145
int32_t width;
int32_t height;
const KDColor image_data;
};
BackgroundView::BackgroundView():
m_data(nullptr),
m_isDataValid(false),
m_defaultColor(Palette::BackgroundHard)
{
}
void BackgroundView::setBackgroundImage(const uint8_t * data) {
m_data = data;
updateDataValidity();
markRectAsDirty(bounds());
}
void BackgroundView::setDefaultColor(KDColor defaultColor) {
m_defaultColor = defaultColor;
}
void BackgroundView::drawRect(KDContext * ctx, KDRect rect) const {
if(!m_isDataValid) {
ctx->fillRect(rect, m_defaultColor);
return;
}
OBMHeader* h = (OBMHeader*)m_data;
int yrectToImage = ctx->origin().y() - m_frame.y();
int xrectToImage = ctx->origin().x() - m_frame.x();
for (int line = rect.y(); line <= rect.bottom(); line++) {
int offset = ((line + yrectToImage) * h->width) + (rect.x() + xrectToImage);
ctx->fillRectWithPixels(KDRect(rect.x(), line, rect.width(), 1), &(h->image_data) + offset, nullptr);
}
}
void BackgroundView::updateDataValidity() {
if(m_data == nullptr || KDIonContext::sharedContext()->gammaEnabled) {
m_isDataValid = false;
return;
}
OBMHeader* h = (OBMHeader*)m_data;
m_isDataValid = h->signature==466512775 && h->height==m_frame.height() && h->width==m_frame.width();
}

79
escher/src/icon_view.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include <escher/icon_view.h>
extern "C" {
#include <assert.h>
}
#include <ion.h>
#include <kandinsky.h>
IconView::IconView() :
View(),
m_image(nullptr)
{
}
constexpr static int iconBufferSize = 3080;
// Icon file is 55 x 56 = 3080
void IconView::drawRect(KDContext * ctx, KDRect rect) const {
const uint8_t* data;
size_t size;
if (m_image != nullptr) {
assert(bounds().width() == m_image->width());
assert(bounds().height() == m_image->height());
assert(bounds().width() == 55);
assert(bounds().height() == 56);
data = m_image->compressedPixelData();
size = m_image->compressedPixelDataSize();
} else if (m_data != nullptr) {
data = m_data;
size = m_dataLength;
} else {
return;
}
KDColor pixelBuffer[iconBufferSize];
assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack
Ion::decompress(
data,
reinterpret_cast<uint8_t *>(pixelBuffer),
size,
iconBufferSize * sizeof(KDColor)
);
//We push the first 6 lines of the image so that they are truncated on the sides
ctx->fillRectWithPixels(KDRect(6, 0, m_frame.width()-12, 1),pixelBuffer+6, nullptr);
ctx->fillRectWithPixels(KDRect(4, 1, m_frame.width()-8, 1),pixelBuffer+6+55, nullptr);
ctx->fillRectWithPixels(KDRect(3, 2, m_frame.width()-6, 1),pixelBuffer+3+(2*55), nullptr);
ctx->fillRectWithPixels(KDRect(2, 3, m_frame.width()-4, 1),pixelBuffer+2+(3*55), nullptr);
ctx->fillRectWithPixels(KDRect(1, 4, m_frame.width()-2, 1),pixelBuffer+1+(4*55), nullptr);
ctx->fillRectWithPixels(KDRect(1, 5, m_frame.width()-2, 1),pixelBuffer+1+(5*55), nullptr);
//Then we push the rectangular part of the image
ctx->fillRectWithPixels(KDRect(0, 6, m_frame.width(), 44),pixelBuffer+(6*55), nullptr);
//Finaly we push the last 5 lines of the image so that they are truncated on the sides
ctx->fillRectWithPixels(KDRect(1, 50, m_frame.width()-2, 1),pixelBuffer+1+(50*55), nullptr);
ctx->fillRectWithPixels(KDRect(1, 51, m_frame.width()-2, 1),pixelBuffer+1+(51*55), nullptr);
ctx->fillRectWithPixels(KDRect(2, 52, m_frame.width()-4, 1),pixelBuffer+2+(52*55), nullptr);
ctx->fillRectWithPixels(KDRect(3, 53, m_frame.width()-6, 1),pixelBuffer+3+(53*55), nullptr);
ctx->fillRectWithPixels(KDRect(4, 54, m_frame.width()-8, 1),pixelBuffer+4+(54*55), nullptr);
ctx->fillRectWithPixels(KDRect(6, 55, m_frame.width()-12, 1),pixelBuffer+6+(55*55), nullptr);
}
void IconView::setImage(const uint8_t *data, size_t dataLength) {
if (data != m_data && dataLength != m_dataLength) {
m_data = data;
m_dataLength = dataLength;
markRectAsDirty(bounds());
}
}
void IconView::setImage(const Image * image) {
if (image != m_image) {
m_image = image;
markRectAsDirty(bounds());
}
}

View File

@@ -9,12 +9,12 @@ extern "C" {
ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) :
View(),
m_contentView(contentView),
m_innerView(this),
m_dataSource(dataSource),
m_topMargin(0),
m_rightMargin(0),
m_bottomMargin(0),
m_leftMargin(0),
m_innerView(this),
m_decorators(),
m_backgroundColor(Palette::BackgroundApps)
{
@@ -24,12 +24,12 @@ ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) :
ScrollView::ScrollView(ScrollView&& other) :
m_contentView(other.m_contentView),
m_innerView(this),
m_dataSource(other.m_dataSource),
m_topMargin(other.m_topMargin),
m_rightMargin(other.m_rightMargin),
m_bottomMargin(other.m_bottomMargin),
m_leftMargin(other.m_leftMargin),
m_innerView(this),
m_backgroundColor(other.m_backgroundColor)
{
setDecoratorType(other.m_decoratorType);
@@ -144,7 +144,7 @@ void ScrollView::layoutSubviews(bool force) {
if (!r2.isEmpty()) {
markRectAsDirty(r2);
}
m_innerView.setFrame(innerFrame, force);
getInnerView()->setFrame(innerFrame, force);
KDPoint absoluteOffset = contentOffset().opposite().translatedBy(KDPoint(m_leftMargin - innerFrame.x(), m_topMargin - innerFrame.y()));
KDRect contentFrame = KDRect(absoluteOffset, contentSize());
m_contentView->setFrame(contentFrame, force);