From 0366c4cd00c9d55bcf8ce2e2ba6bf251b3179386 Mon Sep 17 00:00:00 2001 From: Laury Date: Tue, 6 Jul 2021 14:54:55 +0200 Subject: [PATCH] [home] Added support for a wallpaper in a special format (.obm) --- apps/home/Makefile | 1 + apps/home/app_cell.cpp | 9 ++- apps/home/app_cell.h | 4 +- apps/home/controller.cpp | 27 ++++++- apps/home/controller.h | 5 +- .../selectable_table_view_with_background.cpp | 16 ++++ .../selectable_table_view_with_background.h | 25 ++++++ escher/Makefile | 2 + escher/include/escher.h | 2 + escher/include/escher/background_view.h | 19 +++++ escher/include/escher/icon_view.h | 20 +++++ escher/include/escher/scroll_view.h | 41 +++++----- escher/src/background_view.cpp | 65 +++++++++++++++ escher/src/icon_view.cpp | 79 +++++++++++++++++++ escher/src/scroll_view.cpp | 6 +- 15 files changed, 291 insertions(+), 30 deletions(-) create mode 100644 apps/home/selectable_table_view_with_background.cpp create mode 100644 apps/home/selectable_table_view_with_background.h create mode 100644 escher/include/escher/background_view.h create mode 100644 escher/include/escher/icon_view.h create mode 100644 escher/src/background_view.cpp create mode 100644 escher/src/icon_view.cpp diff --git a/apps/home/Makefile b/apps/home/Makefile index c76c45808..880302a52 100644 --- a/apps/home/Makefile +++ b/apps/home/Makefile @@ -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) diff --git a/apps/home/app_cell.cpp b/apps/home/app_cell.cpp index a6e5a1554..777bc14f6 100644 --- a/apps/home/app_cell.cpp +++ b/apps/home/app_cell.cpp @@ -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); diff --git a/apps/home/app_cell.h b/apps/home/app_cell.h index 89c22e008..f9ff029bd 100644 --- a/apps/home/app_cell.h +++ b/apps/home/app_cell.h @@ -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; }; diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp index 2724bd172..df725f10f 100644 --- a/apps/home/controller.cpp +++ b/apps/home/controller.cpp @@ -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(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) { diff --git a/apps/home/controller.h b/apps/home/controller.h index 90a5604a3..3301fac78 100644 --- a/apps/home/controller.h +++ b/apps/home/controller.h @@ -2,6 +2,7 @@ #define HOME_CONTROLLER_H #include +#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; diff --git a/apps/home/selectable_table_view_with_background.cpp b/apps/home/selectable_table_view_with_background.cpp new file mode 100644 index 000000000..66854589f --- /dev/null +++ b/apps/home/selectable_table_view_with_background.cpp @@ -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); +} \ No newline at end of file diff --git a/apps/home/selectable_table_view_with_background.h b/apps/home/selectable_table_view_with_background.h new file mode 100644 index 000000000..b722cf20b --- /dev/null +++ b/apps/home/selectable_table_view_with_background.h @@ -0,0 +1,25 @@ +#ifndef ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H +#define ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H + +#include +#include + +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 diff --git a/escher/Makefile b/escher/Makefile index 5a4a0fe87..a8bb8491c 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -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 \ diff --git a/escher/include/escher.h b/escher/include/escher.h index e07ce1abb..62ceb7e1a 100644 --- a/escher/include/escher.h +++ b/escher/include/escher.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/escher/include/escher/background_view.h b/escher/include/escher/background_view.h new file mode 100644 index 000000000..697d7f27d --- /dev/null +++ b/escher/include/escher/background_view.h @@ -0,0 +1,19 @@ +#ifndef ESCHER_BACKGROUND_VIEW_H +#define ESCHER_BACKGROUND_VIEW_H + +#include + +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 diff --git a/escher/include/escher/icon_view.h b/escher/include/escher/icon_view.h new file mode 100644 index 000000000..4f03a140a --- /dev/null +++ b/escher/include/escher/icon_view.h @@ -0,0 +1,20 @@ +#ifndef ESCHER_ICON_VIEW_H +#define ESCHER_ICON_VIEW_H + +#include +#include + +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 diff --git a/escher/include/escher/scroll_view.h b/escher/include/escher/scroll_view.h index 34225273e..8a2761059 100644 --- a/escher/include/escher/scroll_view.h +++ b/escher/include/escher/scroll_view.h @@ -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(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(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: diff --git a/escher/src/background_view.cpp b/escher/src/background_view.cpp new file mode 100644 index 000000000..3711185c0 --- /dev/null +++ b/escher/src/background_view.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include //AND THIS +#include "apps/home/controller.h" +#ifdef HOME_DISPLAY_EXTERNALS +#include "apps/external/external_icon.h" +#include "apps/external/archive.h" +#include +#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(); +} \ No newline at end of file diff --git a/escher/src/icon_view.cpp b/escher/src/icon_view.cpp new file mode 100644 index 000000000..0637d62c6 --- /dev/null +++ b/escher/src/icon_view.cpp @@ -0,0 +1,79 @@ +#include +extern "C" { +#include +} +#include +#include + +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(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()); + } +} diff --git a/escher/src/scroll_view.cpp b/escher/src/scroll_view.cpp index 9cb783d99..a653ae4cf 100644 --- a/escher/src/scroll_view.cpp +++ b/escher/src/scroll_view.cpp @@ -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);