Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
bf5c655
Added new app
yannickulrich Oct 17, 2022
c712c7b
Pass filesystem
yannickulrich Oct 17, 2022
e2bdfc4
List directory
yannickulrich Oct 17, 2022
bb5c5da
only count files in DirRead
yannickulrich Oct 17, 2022
cf9825b
Renamed app
yannickulrich Oct 17, 2022
a619ff8
Added ImageView
yannickulrich Oct 17, 2022
e966407
Pass path to ImageView
yannickulrich Oct 17, 2022
d7328f5
Added open function
yannickulrich Oct 17, 2022
20548aa
Keep track of open image
yannickulrich Oct 17, 2022
ecde3c5
Fixed OBOE in open
yannickulrich Oct 17, 2022
d29dc3a
Open first image by default
yannickulrich Oct 17, 2022
c6d3adc
Pass full path to ImageView
yannickulrich Oct 17, 2022
74b21e5
Show image
yannickulrich Oct 17, 2022
2d73865
Added swiping through images
yannickulrich Oct 17, 2022
2515609
Store a human readable name in ImageView
yannickulrich Oct 18, 2022
73fe366
Show and hide label
yannickulrich Oct 18, 2022
7cc90f8
Pass i and n to ImageView
yannickulrich Oct 18, 2022
ea938da
Added page indicator
yannickulrich Oct 18, 2022
86700f5
Renamed ImageView
yannickulrich Oct 18, 2022
b91490c
New abstract class FileView
yannickulrich Oct 18, 2022
e45ddce
Check file extension
yannickulrich Oct 18, 2022
cff6e89
Added class for text files
yannickulrich Oct 18, 2022
371b32f
Open and read text file
yannickulrich Oct 18, 2022
843e021
Fixed z index
yannickulrich Oct 19, 2022
4082dd3
Ran clang-format
yannickulrich Oct 20, 2022
1935aff
Switched to CamelCase
yannickulrich Oct 28, 2022
519d761
1. Added PageIndicator::CreateHorizontal
yannickulrich Nov 5, 2022
b4b96f6
2. Used page indicator
yannickulrich Nov 5, 2022
38bbe60
3. Added PageIndicator::Hide
yannickulrich Nov 5, 2022
6b4b11e
4. Re-implemented FileView::HideInfo
yannickulrich Nov 5, 2022
46f820c
Avoid temp. double allocation of text buffer
yannickulrich Dec 31, 2022
3be86cf
Check file properly
yannickulrich Dec 31, 2022
9d32c32
New concept: index file instead of DirRead
yannickulrich Dec 31, 2022
b29a044
Top-align textview
yannickulrich Mar 3, 2023
c01bea9
Revert "New concept: index file instead of DirRead"
yannickulrich Mar 12, 2023
480eb14
Removed assert
yannickulrich Mar 12, 2023
12b62fa
Disabled sliding animation
yannickulrich Mar 12, 2023
fb0e206
Stop passing app around
yannickulrich Mar 12, 2023
a6ba3f9
Fixed Werrors
yannickulrich Mar 12, 2023
370ecde
Fixed format
yannickulrich May 27, 2023
3341114
Defined app trait
yannickulrich Dec 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ list(APPEND SOURCE_FILES
displayapp/DisplayApp.cpp
displayapp/screens/Screen.cpp
displayapp/screens/Tile.cpp
displayapp/screens/FileView.cpp
displayapp/screens/Gallery.cpp
displayapp/screens/InfiniPaint.cpp
displayapp/screens/Paddle.cpp
displayapp/screens/StopWatch.cpp
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "displayapp/screens/ApplicationList.h"
#include "displayapp/screens/FirmwareUpdate.h"
#include "displayapp/screens/FirmwareValidation.h"
#include "displayapp/screens/Gallery.h"
#include "displayapp/screens/InfiniPaint.h"
#include "displayapp/screens/Paddle.h"
#include "displayapp/screens/StopWatch.h"
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/Apps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace Pinetime {
Steps,
Dice,
Weather,
Gallery,
PassKey,
QuickSettings,
Settings,
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/fonts/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a, 0xf3ed"
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a, 0xf3ed, 0xf302"
}
],
"bpp": 1,
Expand Down
110 changes: 110 additions & 0 deletions src/displayapp/screens/FileView.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include <nrf_log.h>
#include "displayapp/screens/FileView.h"
#include "displayapp/DisplayApp.h"
#include "displayapp/InfiniTimeTheme.h"

using namespace Pinetime::Applications::Screens;

FileView::FileView(uint8_t screenID, uint8_t nScreens, const char* path)
: Screen(), pageIndicator(screenID, nScreens), screenID(screenID), nScreens(nScreens) {
label = nullptr;

const char* c = strrchr(path, '/') + 1;
if (c == nullptr)
c = path;

strncpy(name, c, LFS_NAME_MAX - 1);
char* pchar = strchr(name, '_');
while (pchar != nullptr) {
*pchar = ' ';
pchar = strchr(pchar + 1, '_');
}
}

void FileView::ShowInfo() {
if (label != nullptr) {
return;
}
label = lv_btn_create(lv_scr_act(), nullptr);
label->user_data = this;

lv_obj_set_height(label, 20);
lv_obj_set_width(label, LV_HOR_RES);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0);

lv_obj_t* txtMessage = lv_label_create(label, nullptr);
lv_obj_set_style_local_bg_color(label, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
lv_label_set_text_static(txtMessage, name);

pageIndicator.CreateHorizontal();
}

void FileView::HideInfo() {
lv_obj_del(label);
pageIndicator.Hide();

label = nullptr;
}

void FileView::ToggleInfo() {
if (label == nullptr)
ShowInfo();
else
HideInfo();
}

FileView::~FileView() {
lv_obj_clean(lv_scr_act());
}

ImageView::ImageView(uint8_t screenID, uint8_t nScreens, const char* path) : FileView(screenID, nScreens, path) {
lv_obj_t* image = lv_img_create(lv_scr_act(), nullptr);
lv_img_set_src(image, path);
lv_obj_align(image, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);

ShowInfo();
}

TextView::TextView(uint8_t screenID, uint8_t nScreens, const char* path, Pinetime::Controllers::FS& fs)
: FileView(screenID, nScreens, path) {

lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(label, LV_LABEL_LONG_BREAK);
lv_obj_set_width(label, LV_HOR_RES);

lfs_info info = {0};
if (fs.Stat(path + 2, &info) != LFS_ERR_OK) {
lv_label_set_text_static(label, "could not stat file");
return;
}
if (info.type != LFS_TYPE_REG) {
lv_label_set_text_static(label, "not a file");
return;
}

buf = (char*) lv_mem_alloc(info.size);
if (buf == nullptr) {
lv_label_set_text_static(label, "could not allocate buffer");
return;
}

lfs_file_t fp;
if (fs.FileOpen(&fp, path + 2, LFS_O_RDONLY) != LFS_ERR_OK) {
lv_label_set_text_static(label, "could not open file");
lv_mem_free(buf);
return;
}

fs.FileRead(&fp, reinterpret_cast<uint8_t*>(buf), info.size);
lv_label_set_text_static(label, buf);

fs.FileClose(&fp);

ShowInfo();
}

TextView::~TextView() {
if (buf != nullptr)
lv_mem_free(buf);
lv_obj_clean(lv_scr_act());
}
43 changes: 43 additions & 0 deletions src/displayapp/screens/FileView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include "displayapp/screens/Screen.h"
#include "displayapp/DisplayApp.h"
#include "displayapp/widgets/PageIndicator.h"
#include <lvgl/lvgl.h>

namespace Pinetime {
namespace Applications {
namespace Screens {
class FileView : public Screen {
public:
FileView(uint8_t screenID, uint8_t nScreens, const char* path);
~FileView() override;

void ShowInfo();
void HideInfo();
void ToggleInfo();

private:
char name[LFS_NAME_MAX];
lv_obj_t* label;

Widgets::PageIndicator pageIndicator;
uint8_t screenID, nScreens;
};

class ImageView : public FileView {
public:
ImageView(uint8_t screenID, uint8_t nScreens, const char* path);
};

class TextView : public FileView {
public:
TextView(uint8_t screenID, uint8_t nScreens, const char* path, Pinetime::Controllers::FS& fs);
~TextView() override;

private:
char* buf;
};
}
}
}
117 changes: 117 additions & 0 deletions src/displayapp/screens/Gallery.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include <nrf_log.h>
#include "displayapp/screens/Gallery.h"
#include "displayapp/DisplayApp.h"

using namespace Pinetime::Applications::Screens;

Gallery::Gallery(Pinetime::Controllers::FS& filesystem) : Screen(), filesystem(filesystem) {
ListDir();

if (nScreens == 0) {
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(title, "no images found");
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
lv_obj_align(title, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
} else {
Open(0);
}
}

Gallery::~Gallery() {
lv_obj_clean(lv_scr_act());
}

bool Gallery::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
switch (event) {
case Pinetime::Applications::TouchEvents::SwipeRight:
return Open(index - 1);
case Pinetime::Applications::TouchEvents::SwipeLeft:
return Open(index + 1);
case Pinetime::Applications::TouchEvents::LongTap:
case Pinetime::Applications::TouchEvents::DoubleTap:
current->ToggleInfo();
return true;
case Pinetime::Applications::TouchEvents::None:
case Pinetime::Applications::TouchEvents::Tap:
case Pinetime::Applications::TouchEvents::SwipeUp:
case Pinetime::Applications::TouchEvents::SwipeDown:
return false;
}
return false;
}

void Gallery::ListDir() {
lfs_dir_t dir = {0};
lfs_info info = {0};
nScreens = 0;

int res = filesystem.DirOpen(directory, &dir);
if (res != 0) {
NRF_LOG_INFO("[Gallery] can't find directory");
return;
}
while (filesystem.DirRead(&dir, &info)) {
if (info.type == LFS_TYPE_DIR)
continue;
nScreens++;
}
res = filesystem.DirClose(&dir);
if (res != 0) {
NRF_LOG_INFO("[Gallery] DirClose failed");
return;
}
}

bool Gallery::Open(int n) {
if ((n < 0) || (n >= nScreens))
return false;

index = n;

lfs_dir_t dir = {0};
lfs_info info = {0};

int res = filesystem.DirOpen(directory, &dir);
if (res != 0) {
NRF_LOG_INFO("[Gallery] can't find directory");
return false;
}
int i = 0;
while (filesystem.DirRead(&dir, &info)) {
if (info.type == LFS_TYPE_DIR)
continue;
if (n == i)
break;
i++;
}
res = filesystem.DirClose(&dir);
if (res != 0) {
NRF_LOG_INFO("[Gallery] DirClose failed");
return false;
}

if (current != nullptr) {
current.reset(nullptr);
}

char fullname[LFS_NAME_MAX] = "F:";
strncat(fullname, directory, sizeof(fullname) - 2 - 1);
strncat(fullname, info.name, sizeof(fullname) - strlen(directory) - 2 - 1);

if (StringEndsWith(fullname, ".bin")) {
current = std::make_unique<ImageView>(n, nScreens, fullname);
} else if (StringEndsWith(fullname, ".txt")) {
current = std::make_unique<TextView>(n, nScreens, fullname, filesystem);
} else {
return false;
}

return true;
}

int Gallery::StringEndsWith(const char* str, const char* suffix) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it belongs to some util file and have a more generic name. But maybe it is fine for now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this but I don't think that we currently have util file. Of course I could create one but I wouldn't want to disrupt any plans the maintainers might have. I would argue that this PR should be as minimal as possible, even if that means that code needs to be moved around later on.

int str_len = strlen(str);
int suffix_len = strlen(suffix);

return (str_len >= suffix_len) && (0 == strcmp(str + (str_len - suffix_len), suffix));
}
47 changes: 47 additions & 0 deletions src/displayapp/screens/Gallery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include "displayapp/screens/Screen.h"
#include "displayapp/screens/FileView.h"
#include "displayapp/DisplayApp.h"
#include <lvgl/lvgl.h>
#include "Symbols.h"

namespace Pinetime {
namespace Applications {
namespace Screens {
class Gallery : public Screen {
public:
static constexpr const char* directory = "/gallery/";

Gallery(Pinetime::Controllers::FS& filesystem);
~Gallery() override;
bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;

private:
int StringEndsWith(const char* str, const char* suffix);

Pinetime::Controllers::FS& filesystem;
std::unique_ptr<FileView> current;

void ListDir();
bool Open(int n);
int nScreens;
int index;
};
}

template <>
struct AppTraits<Apps::Gallery> {
static constexpr Apps app = Apps::Gallery;
static constexpr const char* icon = Screens::Symbols::gallery;

static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Gallery(controllers.filesystem);
};

static bool IsAvailable(Pinetime::Controllers::FS& filesystem) {
return true;
};
};
}
}
1 change: 1 addition & 0 deletions src/displayapp/screens/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace Pinetime {
static constexpr const char* check = "\xEF\x95\xA0";
static constexpr const char* music = "\xEF\x80\x81";
static constexpr const char* tachometer = "\xEF\x8F\xBD";
static constexpr const char* gallery = "\xEF\x8C\x82";
static constexpr const char* paintbrush = "\xEF\x87\xBC";
static constexpr const char* paddle = "\xEF\x91\x9D";
static constexpr const char* map = "\xEF\x96\xa0";
Expand Down
33 changes: 33 additions & 0 deletions src/displayapp/widgets/PageIndicator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,36 @@ void PageIndicator::SetPageIndicatorPosition(uint8_t position) {

lv_line_set_points(pageIndicator, pageIndicatorPoints, 2);
}

void PageIndicator::CreateHorizontal() {
pageIndicatorBasePoints[0].x = 0;
pageIndicatorBasePoints[0].y = 1;
pageIndicatorBasePoints[1].x = LV_HOR_RES - 1;
pageIndicatorBasePoints[1].y = 1;

pageIndicatorBase = lv_line_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, Colors::bgDark);
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);

const int16_t indicatorSize = LV_HOR_RES / nScreens;
const int16_t indicatorPos = indicatorSize * nCurrentScreen;

pageIndicatorPoints[0].x = indicatorPos;
pageIndicatorPoints[0].y = 1;
pageIndicatorPoints[1].x = indicatorPos + indicatorSize;
pageIndicatorPoints[1].y = 1;

pageIndicator = lv_line_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
lv_obj_set_style_local_line_color(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
lv_line_set_points(pageIndicator, pageIndicatorPoints, 2);
}

void PageIndicator::Hide() {
lv_obj_del(pageIndicatorBase);
lv_obj_del(pageIndicator);

pageIndicatorBase = nullptr;
pageIndicator = nullptr;
}
Loading