Compare commits
17
Commits
release/1.5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2754a5ff01 | ||
|
|
970b2c6ca1 | ||
|
|
b6ce599b20 | ||
|
|
f54eab2725 | ||
|
|
0d4c9ab91b | ||
|
|
0daa9db243 | ||
|
|
a2f2eea79e | ||
|
|
86a9b9c4a2 | ||
|
|
ba44978ac4 | ||
|
|
555f76da88 | ||
|
|
0a57c0a5a7 | ||
|
|
a09aef0889 | ||
|
|
8626d69f46 | ||
|
|
fc89e57e69 | ||
|
|
487613b082 | ||
|
|
8d5b119644 | ||
|
|
7271c00d35 |
+31
-7
@@ -122,19 +122,43 @@ A **Wi-Fi signal strength indicator** (dBm) is displayed on-screen during joined
|
|||||||
|
|
||||||
CrossPoint supports sending books from Calibre using the CrossPoint Reader device plugin.
|
CrossPoint supports sending books from Calibre using the CrossPoint Reader device plugin.
|
||||||
|
|
||||||
1. Install the plugin in Calibre:
|
#### Installing the Plugin in Calibre
|
||||||
|
|
||||||
- Head to https://github.com/crosspoint-reader/calibre-plugins/releases to download the latest version of the crosspoint_reader plugin.
|
If you don't already have the plugin installed:
|
||||||
|
|
||||||
- Download the zip file.
|
1. Head to https://github.com/crosspoint-reader/calibre-plugins/releases to download the latest version of the crosspoint_reader plugin.
|
||||||
|
2. Download the zip file.
|
||||||
|
3. Open Calibre → Preferences → Plugins → Load plugin from file → Select the zip file.
|
||||||
|
4. Restart Calibre.
|
||||||
|
|
||||||
- Open Calibre → Preferences → Plugins → Load plugin from file → Select the zip file.
|
#### Configuring the CrossPoint Plugin in Calibre
|
||||||
|
1. In Calibre select Preferences.
|
||||||
|
2. In the Preferences dialog select Plugins.
|
||||||
|
3. In Plugins search for "crosspoint".
|
||||||
|
4. Click on "Customize plugin".
|
||||||
|
5. Update the value for "Host" to match the IP for your device.
|
||||||
|
6. Leave the other settings as they are.
|
||||||
|
7. [optional] Modify the "Upload path" to point to a subfolder other than the root "/" folder. Enter this as a path relative to the root folder. Example: `/mybooks`
|
||||||
|
8. Restart Calibre.
|
||||||
|
|
||||||
2. On the device: File Transfer -> Calibre Wireless, then join a network.
|
<img width="420" height="385" alt="Image" src="https://github.com/user-attachments/assets/01fc7e33-a9a7-48ba-9e26-2e68d1f9daec" />
|
||||||
|
|
||||||
3. Make sure your computer is on the same Wi-Fi network.
|
#### Uploading Books
|
||||||
|
|
||||||
4. In Calibre, click "Send to device" to transfer books.
|
To upload a book using the CrossPoint plugin in Calibre:
|
||||||
|
|
||||||
|
1. On the device: File Transfer -> Calibre Wireless, then join a network.
|
||||||
|
2. Select one or more books.
|
||||||
|
3. Right-click on that selection.
|
||||||
|
4. Select "Send to Device" > "Send to main memory"
|
||||||
|
|
||||||
|
The CrossPoint plugin will connect to your device, create a folder for the book's author in the root folder (or the folder you configured for the plugin), then copy the book into that folder.
|
||||||
|
|
||||||
|
<img width="783" height="310" alt="Image" src="https://github.com/user-attachments/assets/741b0909-2e1d-4f16-8af0-2c43fbda5ce6" />
|
||||||
|
|
||||||
|
#### Removing a Book
|
||||||
|
|
||||||
|
Books cannot be removed from your device through Calibre. Use the web interface instead.
|
||||||
|
|
||||||
### 3.6 Settings
|
### 3.6 Settings
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,15 @@ families:
|
|||||||
italic: {url: "https://raw.githubusercontent.com/impallari/Libre-Baskerville/master/fonts/ttf/LibreBaskerville-Italic.ttf"}
|
italic: {url: "https://raw.githubusercontent.com/impallari/Libre-Baskerville/master/fonts/ttf/LibreBaskerville-Italic.ttf"}
|
||||||
bolditalic: {url: "https://raw.githubusercontent.com/impallari/Libre-Baskerville/master/fonts/ttf/LibreBaskerville-BoldItalic.ttf"}
|
bolditalic: {url: "https://raw.githubusercontent.com/impallari/Libre-Baskerville/master/fonts/ttf/LibreBaskerville-BoldItalic.ttf"}
|
||||||
|
|
||||||
|
- name: Vollkorn
|
||||||
|
description: "A serif for bread and butter use by Friedrich Althausen (Latin, Greek, Cyrillic)"
|
||||||
|
intervals: latin-ext,greek,cyrillic
|
||||||
|
sizes: [12, 14, 16, 18]
|
||||||
|
styles:
|
||||||
|
regular: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/vollkorn/Vollkorn%5Bwght%5D.ttf", variable: {wght: 400}}
|
||||||
|
bold: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/vollkorn/Vollkorn%5Bwght%5D.ttf", variable: {wght: 700}}
|
||||||
|
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/vollkorn/Vollkorn-Italic%5Bwght%5D.ttf", variable: {wght: 400}}
|
||||||
|
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/vollkorn/Vollkorn-Italic%5Bwght%5D.ttf", variable: {wght: 700}}
|
||||||
|
|
||||||
# ── Sans-serif ─────────────────────────────────────────────────────────
|
# ── Sans-serif ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
+22
-14
@@ -3,6 +3,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace FsHelpers {
|
namespace FsHelpers {
|
||||||
@@ -36,12 +37,14 @@ std::string decodeUriEscapes(const std::string& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string normalisePath(const std::string& path) {
|
std::string normalisePath(const std::string& path) {
|
||||||
std::vector<std::string> components;
|
std::vector<std::string_view> components;
|
||||||
std::string component;
|
components.reserve(8); // Eight nested folders is more than we might expect
|
||||||
|
|
||||||
for (const auto c : path) {
|
size_t start = 0;
|
||||||
if (c == '/') {
|
for (size_t i = 0; i <= path.length(); ++i) {
|
||||||
if (!component.empty()) {
|
if (i == path.length() || path[i] == '/') {
|
||||||
|
if (i > start) {
|
||||||
|
std::string_view component(path.data() + start, i - start);
|
||||||
if (component == "..") {
|
if (component == "..") {
|
||||||
if (!components.empty()) {
|
if (!components.empty()) {
|
||||||
components.pop_back();
|
components.pop_back();
|
||||||
@@ -49,23 +52,28 @@ std::string normalisePath(const std::string& path) {
|
|||||||
} else {
|
} else {
|
||||||
components.push_back(component);
|
components.push_back(component);
|
||||||
}
|
}
|
||||||
component.clear();
|
|
||||||
}
|
}
|
||||||
} else {
|
start = i + 1;
|
||||||
component += c;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!component.empty()) {
|
if (components.empty()) {
|
||||||
components.push_back(component);
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_len = 0;
|
||||||
|
for (const auto& c : components) {
|
||||||
|
total_len += c.length() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
for (const auto& c : components) {
|
result.reserve(total_len - 1);
|
||||||
if (!result.empty()) {
|
|
||||||
result += "/";
|
for (size_t i = 0; i < components.size(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
result += '/';
|
||||||
}
|
}
|
||||||
result += c;
|
result.append(components[i].data(), components[i].length());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+185
-15
@@ -661,8 +661,10 @@ void GfxRenderer::drawRoundedRect(const int x, const int y, const int width, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const {
|
void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const {
|
||||||
for (int fillY = y; fillY < y + height; fillY++) {
|
if (state) {
|
||||||
drawLine(x, fillY, x + width - 1, fillY, state);
|
fillRectImpl<Color::Black>(x, y, width, height);
|
||||||
|
} else {
|
||||||
|
fillRectImpl<Color::White>(x, y, width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,26 +696,194 @@ void GfxRenderer::drawPixelDither<Color::DarkGray>(const int x, const int y) con
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::fillRectDither(const int x, const int y, const int width, const int height, Color color) const {
|
void GfxRenderer::fillRectDither(const int x, const int y, const int width, const int height, Color color) const {
|
||||||
if (color == Color::Clear) {
|
switch (color) {
|
||||||
} else if (color == Color::Black) {
|
case Color::Clear:
|
||||||
fillRect(x, y, width, height, true);
|
break;
|
||||||
} else if (color == Color::White) {
|
case Color::Black:
|
||||||
fillRect(x, y, width, height, false);
|
fillRectImpl<Color::Black>(x, y, width, height);
|
||||||
} else if (color == Color::LightGray) {
|
break;
|
||||||
for (int fillY = y; fillY < y + height; fillY++) {
|
case Color::White:
|
||||||
for (int fillX = x; fillX < x + width; fillX++) {
|
fillRectImpl<Color::White>(x, y, width, height);
|
||||||
drawPixelDither<Color::LightGray>(fillX, fillY);
|
break;
|
||||||
|
case Color::LightGray:
|
||||||
|
fillRectImpl<Color::LightGray>(x, y, width, height);
|
||||||
|
break;
|
||||||
|
case Color::DarkGray:
|
||||||
|
fillRectImpl<Color::DarkGray>(x, y, width, height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Color C>
|
||||||
|
void GfxRenderer::fillRectImpl(const int x, const int y, const int width, const int height) const {
|
||||||
|
if constexpr (C == Color::Clear) return;
|
||||||
|
if (width <= 0 || height <= 0) return;
|
||||||
|
if (fontCacheManager_ && fontCacheManager_->isScanning()) return;
|
||||||
|
|
||||||
|
// Clip in logical space.
|
||||||
|
const int screenW = getScreenWidth();
|
||||||
|
const int screenH = getScreenHeight();
|
||||||
|
const int lx0 = std::max(0, x);
|
||||||
|
const int ly0 = std::max(0, y);
|
||||||
|
const int lx1 = std::min(screenW, x + width);
|
||||||
|
const int ly1 = std::min(screenH, y + height);
|
||||||
|
if (lx0 >= lx1 || ly0 >= ly1) return;
|
||||||
|
|
||||||
|
// Rotate the two opposing logical corners into physical-framebuffer space.
|
||||||
|
// The bounding rect in physical space is the rect we need to fill — rotation
|
||||||
|
// is rigid (no shear/stretch) so the bbox of the two corners IS the rect.
|
||||||
|
int paX, paY, pbX, pbY;
|
||||||
|
rotateCoordinates(orientation, lx0, ly0, &paX, &paY, panelWidth, panelHeight);
|
||||||
|
rotateCoordinates(orientation, lx1 - 1, ly1 - 1, &pbX, &pbY, panelWidth, panelHeight);
|
||||||
|
|
||||||
|
const int phyX0 = std::min(paX, pbX);
|
||||||
|
const int phyX1 = std::max(paX, pbX); // inclusive
|
||||||
|
int phyY0 = std::min(paY, pbY);
|
||||||
|
int phyY1 = std::max(paY, pbY);
|
||||||
|
|
||||||
|
// Strip mode: clip Y range to the active band and redirect writes.
|
||||||
|
uint8_t* target = getWriteTarget();
|
||||||
|
const int originY = getWriteOriginY();
|
||||||
|
const int writeRows = getWriteRows();
|
||||||
|
phyY0 = std::max(phyY0, originY);
|
||||||
|
phyY1 = std::min(phyY1, originY + writeRows - 1);
|
||||||
|
if (phyY0 > phyY1) return;
|
||||||
|
|
||||||
|
// Bit/byte layout: MSB-first within a byte, so phyX → bit (7 - (phyX & 7)).
|
||||||
|
// Head and tail masks cover only the in-rect bits of the first/last byte.
|
||||||
|
const int byteStart = phyX0 >> 3;
|
||||||
|
const int byteEnd = phyX1 >> 3; // inclusive
|
||||||
|
const uint8_t headMask = static_cast<uint8_t>(0xFFu >> (phyX0 & 7));
|
||||||
|
const uint8_t tailMask = static_cast<uint8_t>(0xFFu << (7 - (phyX1 & 7)));
|
||||||
|
const int32_t panelStride = static_cast<int32_t>(panelWidthBytes);
|
||||||
|
|
||||||
|
if constexpr (C == Color::Black || C == Color::White) {
|
||||||
|
// Solid fill. Framebuffer: 0 = black, 1 = white.
|
||||||
|
const uint8_t fillByte = (C == Color::Black) ? 0x00u : 0xFFu;
|
||||||
|
for (int py = phyY0; py <= phyY1; ++py) {
|
||||||
|
uint8_t* row = target + static_cast<int32_t>(py - originY) * panelStride;
|
||||||
|
if (byteStart == byteEnd) {
|
||||||
|
const uint8_t mask = headMask & tailMask;
|
||||||
|
if constexpr (C == Color::Black) {
|
||||||
|
row[byteStart] &= static_cast<uint8_t>(~mask);
|
||||||
|
} else {
|
||||||
|
row[byteStart] |= mask;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (C == Color::Black) {
|
||||||
|
row[byteStart] &= static_cast<uint8_t>(~headMask);
|
||||||
|
if (byteEnd > byteStart + 1) {
|
||||||
|
memset(row + byteStart + 1, fillByte, byteEnd - byteStart - 1);
|
||||||
|
}
|
||||||
|
row[byteEnd] &= static_cast<uint8_t>(~tailMask);
|
||||||
|
} else {
|
||||||
|
row[byteStart] |= headMask;
|
||||||
|
if (byteEnd > byteStart + 1) {
|
||||||
|
memset(row + byteStart + 1, fillByte, byteEnd - byteStart - 1);
|
||||||
|
}
|
||||||
|
row[byteEnd] |= tailMask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (color == Color::DarkGray) {
|
} else {
|
||||||
for (int fillY = y; fillY < y + height; fillY++) {
|
// Dither (LightGray / DarkGray). Both patterns have period 2 in logical
|
||||||
for (int fillX = x; fillX < x + width; fillX++) {
|
// (x, y), so per physical row we precompute one byte that represents the
|
||||||
drawPixelDither<Color::DarkGray>(fillX, fillY);
|
// pattern across an 8-pixel stretch — every full byte in the row uses
|
||||||
|
// that same value.
|
||||||
|
//
|
||||||
|
// dlxPerPhyX / dlyPerPhyX: how logical (x, y) change as phyX increments
|
||||||
|
// along a physical row. Derived from inverting rotateCoordinates.
|
||||||
|
int dlxPerPhyX = 0, dlyPerPhyX = 0;
|
||||||
|
switch (orientation) {
|
||||||
|
case Portrait:
|
||||||
|
dlxPerPhyX = 0;
|
||||||
|
dlyPerPhyX = 1;
|
||||||
|
break;
|
||||||
|
case PortraitInverted:
|
||||||
|
dlxPerPhyX = 0;
|
||||||
|
dlyPerPhyX = -1;
|
||||||
|
break;
|
||||||
|
case LandscapeClockwise:
|
||||||
|
dlxPerPhyX = -1;
|
||||||
|
dlyPerPhyX = 0;
|
||||||
|
break;
|
||||||
|
case LandscapeCounterClockwise:
|
||||||
|
dlxPerPhyX = 1;
|
||||||
|
dlyPerPhyX = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dither pattern has period 2 in logical space, and each orientation
|
||||||
|
// maps py to logical coords with a fixed parity relationship. The
|
||||||
|
// blackMask byte therefore repeats with period 2 in py. Precompute both
|
||||||
|
// variants outside the row loop to eliminate the per-row switch + 8-bit
|
||||||
|
// construction loop.
|
||||||
|
uint8_t blackMasks[2];
|
||||||
|
for (int parityIdx = 0; parityIdx < 2; ++parityIdx) {
|
||||||
|
const int samplePy = phyY0 + parityIdx;
|
||||||
|
int lxBase = 0, lyBase = 0;
|
||||||
|
switch (orientation) {
|
||||||
|
case Portrait:
|
||||||
|
lxBase = panelHeight - 1 - samplePy;
|
||||||
|
lyBase = byteStart * 8;
|
||||||
|
break;
|
||||||
|
case PortraitInverted:
|
||||||
|
lxBase = samplePy;
|
||||||
|
lyBase = panelWidth - 1 - byteStart * 8;
|
||||||
|
break;
|
||||||
|
case LandscapeClockwise:
|
||||||
|
lxBase = panelWidth - 1 - byteStart * 8;
|
||||||
|
lyBase = panelHeight - 1 - samplePy;
|
||||||
|
break;
|
||||||
|
case LandscapeCounterClockwise:
|
||||||
|
lxBase = byteStart * 8;
|
||||||
|
lyBase = samplePy;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint8_t mask = 0;
|
||||||
|
for (int b = 0; b < 8; ++b) {
|
||||||
|
const int lx = lxBase + b * dlxPerPhyX;
|
||||||
|
const int ly = lyBase + b * dlyPerPhyX;
|
||||||
|
bool isBlack;
|
||||||
|
if constexpr (C == Color::LightGray) {
|
||||||
|
isBlack = ((lx & 1) == 0) && ((ly & 1) == 0);
|
||||||
|
} else { // DarkGray
|
||||||
|
isBlack = (((lx + ly) & 1) == 0);
|
||||||
|
}
|
||||||
|
if (isBlack) mask |= static_cast<uint8_t>(1u << (7 - b));
|
||||||
|
}
|
||||||
|
blackMasks[samplePy & 1] = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int py = phyY0; py <= phyY1; ++py) {
|
||||||
|
const uint8_t blackMask = blackMasks[py & 1];
|
||||||
|
const uint8_t whiteMask = static_cast<uint8_t>(~blackMask);
|
||||||
|
|
||||||
|
// Dither writes BOTH inks (the slow path called drawPixel for every
|
||||||
|
// pixel — setting or clearing — so we must do the same). Inside the
|
||||||
|
// rect mask: write whiteMask (1s where white, 0s where black). Outside
|
||||||
|
// the rect mask: leave the framebuffer untouched.
|
||||||
|
uint8_t* row = target + static_cast<int32_t>(py - originY) * panelStride;
|
||||||
|
if (byteStart == byteEnd) {
|
||||||
|
const uint8_t rectMask = headMask & tailMask;
|
||||||
|
row[byteStart] = static_cast<uint8_t>((row[byteStart] & ~rectMask) | (rectMask & whiteMask));
|
||||||
|
} else {
|
||||||
|
row[byteStart] = static_cast<uint8_t>((row[byteStart] & ~headMask) | (headMask & whiteMask));
|
||||||
|
if (byteEnd > byteStart + 1) {
|
||||||
|
// Period 2, so every full byte in this row is exactly whiteMask.
|
||||||
|
memset(row + byteStart + 1, whiteMask, byteEnd - byteStart - 1);
|
||||||
|
}
|
||||||
|
row[byteEnd] = static_cast<uint8_t>((row[byteEnd] & ~tailMask) | (tailMask & whiteMask));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template void GfxRenderer::fillRectImpl<Color::Black>(int, int, int, int) const;
|
||||||
|
template void GfxRenderer::fillRectImpl<Color::White>(int, int, int, int) const;
|
||||||
|
template void GfxRenderer::fillRectImpl<Color::LightGray>(int, int, int, int) const;
|
||||||
|
template void GfxRenderer::fillRectImpl<Color::DarkGray>(int, int, int, int) const;
|
||||||
|
|
||||||
void GfxRenderer::maskRoundedRectOutsideCorners(const int x, const int y, const int width, const int height,
|
void GfxRenderer::maskRoundedRectOutsideCorners(const int x, const int y, const int width, const int height,
|
||||||
const int radius, const Color color) const {
|
const int radius, const Color color) const {
|
||||||
if (radius <= 0 || color == Color::Clear) {
|
if (radius <= 0 || color == Color::Clear) {
|
||||||
|
|||||||
@@ -81,6 +81,12 @@ class GfxRenderer {
|
|||||||
void drawPixelDither(int x, int y) const;
|
void drawPixelDither(int x, int y) const;
|
||||||
template <Color color>
|
template <Color color>
|
||||||
void fillArc(int maxRadius, int cx, int cy, int xDir, int yDir) const;
|
void fillArc(int maxRadius, int cx, int cy, int xDir, int yDir) const;
|
||||||
|
// Byte-aligned, orientation-specialized rectangle fill. Rotates the rect's
|
||||||
|
// two opposing corners into physical-framebuffer space once, then walks each
|
||||||
|
// physical row with head-mask / middle memset / tail-mask byte writes — no
|
||||||
|
// per-pixel rotation, no per-pixel RMW.
|
||||||
|
template <Color color>
|
||||||
|
void fillRectImpl(int x, int y, int width, int height) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GfxRenderer(HalDisplay& halDisplay)
|
explicit GfxRenderer(HalDisplay& halDisplay)
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ STR_PAGE_TURN: "Перагортванне"
|
|||||||
STR_PORTRAIT: "Партрэт"
|
STR_PORTRAIT: "Партрэт"
|
||||||
STR_LANDSCAPE_CW: "Ландшафт (CW)"
|
STR_LANDSCAPE_CW: "Ландшафт (CW)"
|
||||||
STR_INVERTED: "Інверсія"
|
STR_INVERTED: "Інверсія"
|
||||||
|
STR_ORIENTATION_INVERTED: "Партрэт 180°"
|
||||||
STR_LANDSCAPE_CCW: "Ландшафт (CCW)"
|
STR_LANDSCAPE_CCW: "Ландшафт (CCW)"
|
||||||
STR_PREV_NEXT: "Назад/Наперад"
|
STR_PREV_NEXT: "Назад/Наперад"
|
||||||
STR_NEXT_PREV: "Наперад/Назад"
|
STR_NEXT_PREV: "Наперад/Назад"
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ STR_PAGE_TURN: "Canvi de pàgina"
|
|||||||
STR_PORTRAIT: "Vertical"
|
STR_PORTRAIT: "Vertical"
|
||||||
STR_LANDSCAPE_CW: "Horitzontal horari"
|
STR_LANDSCAPE_CW: "Horitzontal horari"
|
||||||
STR_INVERTED: "Invertit"
|
STR_INVERTED: "Invertit"
|
||||||
|
STR_ORIENTATION_INVERTED: "Vertical 180°"
|
||||||
STR_LANDSCAPE_CCW: "Horitzontal antihorari"
|
STR_LANDSCAPE_CCW: "Horitzontal antihorari"
|
||||||
STR_PREV_NEXT: "Anterior/Següent"
|
STR_PREV_NEXT: "Anterior/Següent"
|
||||||
STR_NEXT_PREV: "Següent/Anterior"
|
STR_NEXT_PREV: "Següent/Anterior"
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ STR_PAGE_TURN: "Otáčení stránek"
|
|||||||
STR_PORTRAIT: "Na výšku"
|
STR_PORTRAIT: "Na výšku"
|
||||||
STR_LANDSCAPE_CW: "Na šířku po směru hod. ručiček"
|
STR_LANDSCAPE_CW: "Na šířku po směru hod. ručiček"
|
||||||
STR_INVERTED: "Invertovaný"
|
STR_INVERTED: "Invertovaný"
|
||||||
|
STR_ORIENTATION_INVERTED: "Na výšku 180°"
|
||||||
STR_LANDSCAPE_CCW: "Na šířku proti směru hod. ručiček"
|
STR_LANDSCAPE_CCW: "Na šířku proti směru hod. ručiček"
|
||||||
STR_PREV_NEXT: "Předchozí/Další"
|
STR_PREV_NEXT: "Předchozí/Další"
|
||||||
STR_NEXT_PREV: "Další/Předchozí"
|
STR_NEXT_PREV: "Další/Předchozí"
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ STR_PAGE_TURN: "Sideskift"
|
|||||||
STR_PORTRAIT: "Portræt"
|
STR_PORTRAIT: "Portræt"
|
||||||
STR_LANDSCAPE_CW: "Liggende med uret"
|
STR_LANDSCAPE_CW: "Liggende med uret"
|
||||||
STR_INVERTED: "Inverteret"
|
STR_INVERTED: "Inverteret"
|
||||||
|
STR_ORIENTATION_INVERTED: "Portræt 180°"
|
||||||
STR_LANDSCAPE_CCW: "Liggende mod uret"
|
STR_LANDSCAPE_CCW: "Liggende mod uret"
|
||||||
STR_PREV_NEXT: "Forrige/Næste"
|
STR_PREV_NEXT: "Forrige/Næste"
|
||||||
STR_NEXT_PREV: "Næste/Forrige"
|
STR_NEXT_PREV: "Næste/Forrige"
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ STR_SLEEP: "Slaap"
|
|||||||
STR_PAGE_TURN: "Pagina omslaan"
|
STR_PAGE_TURN: "Pagina omslaan"
|
||||||
STR_PORTRAIT: "Staand"
|
STR_PORTRAIT: "Staand"
|
||||||
STR_LANDSCAPE_CW: "Liggend (rechtsom)"
|
STR_LANDSCAPE_CW: "Liggend (rechtsom)"
|
||||||
STR_INVERTED: "Omgekeerd"
|
STR_INVERTED: "Geïnverteerd"
|
||||||
|
STR_ORIENTATION_INVERTED: "Staand 180°"
|
||||||
STR_LANDSCAPE_CCW: "Liggend (linksom)"
|
STR_LANDSCAPE_CCW: "Liggend (linksom)"
|
||||||
STR_PREV_NEXT: "Vorige/Volgende"
|
STR_PREV_NEXT: "Vorige/Volgende"
|
||||||
STR_NEXT_PREV: "Volgende/Vorige"
|
STR_NEXT_PREV: "Volgende/Vorige"
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ STR_FORCE_REFRESH: "Refresh Screen"
|
|||||||
STR_PORTRAIT: "Portrait"
|
STR_PORTRAIT: "Portrait"
|
||||||
STR_LANDSCAPE_CW: "Landscape CW"
|
STR_LANDSCAPE_CW: "Landscape CW"
|
||||||
STR_INVERTED: "Inverted"
|
STR_INVERTED: "Inverted"
|
||||||
|
STR_ORIENTATION_INVERTED: "Portrait 180°"
|
||||||
STR_LANDSCAPE_CCW: "Landscape CCW"
|
STR_LANDSCAPE_CCW: "Landscape CCW"
|
||||||
STR_PREV_NEXT: "Prev/Next"
|
STR_PREV_NEXT: "Prev/Next"
|
||||||
STR_NEXT_PREV: "Next/Prev"
|
STR_NEXT_PREV: "Next/Prev"
|
||||||
|
|||||||
@@ -130,7 +130,8 @@ STR_SLEEP: "Lepotila"
|
|||||||
STR_PAGE_TURN: "Sivunkääntö"
|
STR_PAGE_TURN: "Sivunkääntö"
|
||||||
STR_PORTRAIT: "Pysty"
|
STR_PORTRAIT: "Pysty"
|
||||||
STR_LANDSCAPE_CW: "Vaaka myötäpäivään"
|
STR_LANDSCAPE_CW: "Vaaka myötäpäivään"
|
||||||
STR_INVERTED: "Käännetty"
|
STR_INVERTED: "Käänteinen"
|
||||||
|
STR_ORIENTATION_INVERTED: "Pysty 180°"
|
||||||
STR_LANDSCAPE_CCW: "Vaaka vastapäivään"
|
STR_LANDSCAPE_CCW: "Vaaka vastapäivään"
|
||||||
STR_PREV_NEXT: "Edell/Seur"
|
STR_PREV_NEXT: "Edell/Seur"
|
||||||
STR_NEXT_PREV: "Seur/Edell"
|
STR_NEXT_PREV: "Seur/Edell"
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ STR_PAGE_TURN: "Page suivante"
|
|||||||
STR_PORTRAIT: "Portrait"
|
STR_PORTRAIT: "Portrait"
|
||||||
STR_LANDSCAPE_CW: "Paysage"
|
STR_LANDSCAPE_CW: "Paysage"
|
||||||
STR_INVERTED: "Inversé"
|
STR_INVERTED: "Inversé"
|
||||||
|
STR_ORIENTATION_INVERTED: "Portrait 180°"
|
||||||
STR_LANDSCAPE_CCW: "Paysage inversé"
|
STR_LANDSCAPE_CCW: "Paysage inversé"
|
||||||
STR_PREV_NEXT: "Préc/Suiv"
|
STR_PREV_NEXT: "Préc/Suiv"
|
||||||
STR_NEXT_PREV: "Suiv/Préc"
|
STR_NEXT_PREV: "Suiv/Préc"
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ STR_ORIENTATION: "Leseausrichtung"
|
|||||||
STR_SIDE_BTN_LAYOUT: "Seitliche Tasten (Lesen)"
|
STR_SIDE_BTN_LAYOUT: "Seitliche Tasten (Lesen)"
|
||||||
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Vordere Tasten ausrichten"
|
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Vordere Tasten ausrichten"
|
||||||
STR_LONG_PRESS_BEHAVIOR: "Verhalten bei langem Tastendruck"
|
STR_LONG_PRESS_BEHAVIOR: "Verhalten bei langem Tastendruck"
|
||||||
STR_LONG_PRESS_BEHAVIOR_OFF: "AUS"
|
STR_LONG_PRESS_BEHAVIOR_OFF: "Aus"
|
||||||
STR_LONG_PRESS_BEHAVIOR_SKIP: "Kapitel überspringen"
|
STR_LONG_PRESS_BEHAVIOR_SKIP: "Kapitel überspringen"
|
||||||
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Ausrichtung ändern"
|
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Ausrichtung ändern"
|
||||||
STR_FONT_PREVIEW_TEXT: "Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich"
|
STR_FONT_PREVIEW_TEXT: "Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich"
|
||||||
@@ -79,8 +79,9 @@ STR_FONT_SIZE: "Schriftgröße"
|
|||||||
STR_LINE_SPACING: "Lese-Zeilenabstand"
|
STR_LINE_SPACING: "Lese-Zeilenabstand"
|
||||||
STR_SCREEN_MARGIN: "Lese-Seitenränder"
|
STR_SCREEN_MARGIN: "Lese-Seitenränder"
|
||||||
STR_PARA_ALIGNMENT: "Lese-Absatzausrichtung"
|
STR_PARA_ALIGNMENT: "Lese-Absatzausrichtung"
|
||||||
|
STR_LONG_PRESS_MENU: "Menütaste lang drücken"
|
||||||
STR_HYPHENATION: "Silbentrennung"
|
STR_HYPHENATION: "Silbentrennung"
|
||||||
STR_TIME_TO_SLEEP: "Standby nach"
|
STR_TIME_TO_SLEEP: "Standby-Modus nach"
|
||||||
STR_REFRESH_FREQ: "Anti-Ghosting nach"
|
STR_REFRESH_FREQ: "Anti-Ghosting nach"
|
||||||
STR_KOREADER_SYNC: "KOReader-Synchr."
|
STR_KOREADER_SYNC: "KOReader-Synchr."
|
||||||
STR_CHECK_UPDATES: "Nach Updates suchen"
|
STR_CHECK_UPDATES: "Nach Updates suchen"
|
||||||
@@ -129,7 +130,8 @@ STR_PAGE_TURN: "Umblättern"
|
|||||||
STR_FORCE_REFRESH: "Bildschirm regenerieren"
|
STR_FORCE_REFRESH: "Bildschirm regenerieren"
|
||||||
STR_PORTRAIT: "Hochformat"
|
STR_PORTRAIT: "Hochformat"
|
||||||
STR_LANDSCAPE_CW: "Querformat rechts"
|
STR_LANDSCAPE_CW: "Querformat rechts"
|
||||||
STR_INVERTED: "Hochformat 180°"
|
STR_INVERTED: "Invertiert"
|
||||||
|
STR_ORIENTATION_INVERTED: "Hochformat 180°"
|
||||||
STR_LANDSCAPE_CCW: "Querformat links"
|
STR_LANDSCAPE_CCW: "Querformat links"
|
||||||
STR_PREV_NEXT: "Zurück/Weiter"
|
STR_PREV_NEXT: "Zurück/Weiter"
|
||||||
STR_NEXT_PREV: "Weiter/Zurück"
|
STR_NEXT_PREV: "Weiter/Zurück"
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ STR_PAGE_TURN: "העברת דף"
|
|||||||
STR_FORCE_REFRESH: "רענון מסך מלא"
|
STR_FORCE_REFRESH: "רענון מסך מלא"
|
||||||
STR_PORTRAIT: "לאורך"
|
STR_PORTRAIT: "לאורך"
|
||||||
STR_LANDSCAPE_CW: "לרוחב (ימינה)"
|
STR_LANDSCAPE_CW: "לרוחב (ימינה)"
|
||||||
STR_INVERTED: "הפוך"
|
STR_INVERTED: "היפוך צבעים"
|
||||||
|
STR_ORIENTATION_INVERTED: "לאורך 180°"
|
||||||
STR_LANDSCAPE_CCW: "לרוחב (שמאלה)"
|
STR_LANDSCAPE_CCW: "לרוחב (שמאלה)"
|
||||||
STR_PREV_NEXT: "הקודם/הבא"
|
STR_PREV_NEXT: "הקודם/הבא"
|
||||||
STR_NEXT_PREV: "הבא/הקודם"
|
STR_NEXT_PREV: "הבא/הקודם"
|
||||||
|
|||||||
@@ -132,7 +132,8 @@ STR_SLEEP: "Alvás"
|
|||||||
STR_PAGE_TURN: "Lapozás"
|
STR_PAGE_TURN: "Lapozás"
|
||||||
STR_PORTRAIT: "Álló"
|
STR_PORTRAIT: "Álló"
|
||||||
STR_LANDSCAPE_CW: "Fekvő jobbra"
|
STR_LANDSCAPE_CW: "Fekvő jobbra"
|
||||||
STR_INVERTED: "Fordított"
|
STR_INVERTED: "Invertált"
|
||||||
|
STR_ORIENTATION_INVERTED: "Álló 180°"
|
||||||
STR_LANDSCAPE_CCW: "Fekvő balra"
|
STR_LANDSCAPE_CCW: "Fekvő balra"
|
||||||
STR_PREV_NEXT: "Előző/Következő"
|
STR_PREV_NEXT: "Előző/Következő"
|
||||||
STR_NEXT_PREV: "Következő/Előző"
|
STR_NEXT_PREV: "Következő/Előző"
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ STR_PAGE_TURN: "Cambio pagina"
|
|||||||
STR_FORCE_REFRESH: "Refresh"
|
STR_FORCE_REFRESH: "Refresh"
|
||||||
STR_PORTRAIT: "Verticale"
|
STR_PORTRAIT: "Verticale"
|
||||||
STR_LANDSCAPE_CW: "Orizzontale Dx"
|
STR_LANDSCAPE_CW: "Orizzontale Dx"
|
||||||
STR_INVERTED: "Capovolto"
|
STR_INVERTED: "Invertito"
|
||||||
|
STR_ORIENTATION_INVERTED: "Verticale 180°"
|
||||||
STR_LANDSCAPE_CCW: "Orizzontale Sx"
|
STR_LANDSCAPE_CCW: "Orizzontale Sx"
|
||||||
STR_PREV_NEXT: "Prec/Succ"
|
STR_PREV_NEXT: "Prec/Succ"
|
||||||
STR_NEXT_PREV: "Succ/Prec"
|
STR_NEXT_PREV: "Succ/Prec"
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ STR_SLEEP: "Ұйқы"
|
|||||||
STR_PAGE_TURN: "Бет аудару"
|
STR_PAGE_TURN: "Бет аудару"
|
||||||
STR_PORTRAIT: "Тік бағдар"
|
STR_PORTRAIT: "Тік бағдар"
|
||||||
STR_LANDSCAPE_CW: "Көлденең (сағат бағытымен)"
|
STR_LANDSCAPE_CW: "Көлденең (сағат бағытымен)"
|
||||||
STR_INVERTED: "Төңкерілген"
|
STR_INVERTED: "Инверсия"
|
||||||
|
STR_ORIENTATION_INVERTED: "Тік бағдар 180°"
|
||||||
STR_LANDSCAPE_CCW: "Көлденең (сағат тіліне қарсы)"
|
STR_LANDSCAPE_CCW: "Көлденең (сағат тіліне қарсы)"
|
||||||
STR_PREV_NEXT: "Алдыңғы/Келесі"
|
STR_PREV_NEXT: "Алдыңғы/Келесі"
|
||||||
STR_NEXT_PREV: "Келесі/Алдыңғы"
|
STR_NEXT_PREV: "Келесі/Алдыңғы"
|
||||||
|
|||||||
@@ -132,7 +132,8 @@ STR_SLEEP: "Miegas"
|
|||||||
STR_PAGE_TURN: "Versti psl."
|
STR_PAGE_TURN: "Versti psl."
|
||||||
STR_PORTRAIT: "Stačias"
|
STR_PORTRAIT: "Stačias"
|
||||||
STR_LANDSCAPE_CW: "Gulsčias (P)"
|
STR_LANDSCAPE_CW: "Gulsčias (P)"
|
||||||
STR_INVERTED: "Apverstas"
|
STR_INVERTED: "Invertuotas"
|
||||||
|
STR_ORIENTATION_INVERTED: "Stačias 180°"
|
||||||
STR_LANDSCAPE_CCW: "Gulsčias (A)"
|
STR_LANDSCAPE_CCW: "Gulsčias (A)"
|
||||||
STR_PREV_NEXT: "Atgal/Pirmyn"
|
STR_PREV_NEXT: "Atgal/Pirmyn"
|
||||||
STR_NEXT_PREV: "Pirmyn/Atgal"
|
STR_NEXT_PREV: "Pirmyn/Atgal"
|
||||||
|
|||||||
@@ -136,7 +136,8 @@ STR_PAGE_TURN: "Nast. str."
|
|||||||
STR_FORCE_REFRESH: "Odśwież ekran"
|
STR_FORCE_REFRESH: "Odśwież ekran"
|
||||||
STR_PORTRAIT: "Pionowo"
|
STR_PORTRAIT: "Pionowo"
|
||||||
STR_LANDSCAPE_CW: "Poziomo P"
|
STR_LANDSCAPE_CW: "Poziomo P"
|
||||||
STR_INVERTED: "Odwrócony"
|
STR_INVERTED: "Inwersja"
|
||||||
|
STR_ORIENTATION_INVERTED: "Pionowo 180°"
|
||||||
STR_LANDSCAPE_CCW: "Poziomo L"
|
STR_LANDSCAPE_CCW: "Poziomo L"
|
||||||
STR_PREV_NEXT: "Poprz./Nast."
|
STR_PREV_NEXT: "Poprz./Nast."
|
||||||
STR_NEXT_PREV: "Nast./Poprz."
|
STR_NEXT_PREV: "Nast./Poprz."
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ STR_PAGE_TURN: "Virar página"
|
|||||||
STR_PORTRAIT: "Retrato"
|
STR_PORTRAIT: "Retrato"
|
||||||
STR_LANDSCAPE_CW: "Paisagem H"
|
STR_LANDSCAPE_CW: "Paisagem H"
|
||||||
STR_INVERTED: "Invertido"
|
STR_INVERTED: "Invertido"
|
||||||
|
STR_ORIENTATION_INVERTED: "Retrato 180°"
|
||||||
STR_LANDSCAPE_CCW: "Paisagem AH"
|
STR_LANDSCAPE_CCW: "Paisagem AH"
|
||||||
STR_PREV_NEXT: "Ant/Próx"
|
STR_PREV_NEXT: "Ant/Próx"
|
||||||
STR_NEXT_PREV: "Próx/Ant"
|
STR_NEXT_PREV: "Próx/Ant"
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ STR_PAGE_TURN: "Răsfoire pagină"
|
|||||||
STR_PORTRAIT: "Vertical"
|
STR_PORTRAIT: "Vertical"
|
||||||
STR_LANDSCAPE_CW: "Orizontal dreapta"
|
STR_LANDSCAPE_CW: "Orizontal dreapta"
|
||||||
STR_INVERTED: "Invers"
|
STR_INVERTED: "Invers"
|
||||||
|
STR_ORIENTATION_INVERTED: "Vertical 180°"
|
||||||
STR_LANDSCAPE_CCW: "Orizontal stânga"
|
STR_LANDSCAPE_CCW: "Orizontal stânga"
|
||||||
STR_PREV_NEXT: "Înainte/Înapoi"
|
STR_PREV_NEXT: "Înainte/Înapoi"
|
||||||
STR_NEXT_PREV: "Înapoi/Înainte"
|
STR_NEXT_PREV: "Înapoi/Înainte"
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ STR_FORCE_REFRESH: "Обновление экрана"
|
|||||||
STR_PORTRAIT: "Портрет"
|
STR_PORTRAIT: "Портрет"
|
||||||
STR_LANDSCAPE_CW: "Ландшафт (CW)"
|
STR_LANDSCAPE_CW: "Ландшафт (CW)"
|
||||||
STR_INVERTED: "Инверсия"
|
STR_INVERTED: "Инверсия"
|
||||||
|
STR_ORIENTATION_INVERTED: "Портрет 180°"
|
||||||
STR_LANDSCAPE_CCW: "Ландшафт (CCW)"
|
STR_LANDSCAPE_CCW: "Ландшафт (CCW)"
|
||||||
STR_PREV_NEXT: "Назад/Вперёд"
|
STR_PREV_NEXT: "Назад/Вперёд"
|
||||||
STR_NEXT_PREV: "Вперёд/Назад"
|
STR_NEXT_PREV: "Вперёд/Назад"
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ STR_PAGE_TURN: "Otáčanie stránok"
|
|||||||
STR_FORCE_REFRESH: "Obnoviť obrazovku"
|
STR_FORCE_REFRESH: "Obnoviť obrazovku"
|
||||||
STR_PORTRAIT: "Na výšku"
|
STR_PORTRAIT: "Na výšku"
|
||||||
STR_LANDSCAPE_CW: "Na šírku v smere hodinových ručičiek"
|
STR_LANDSCAPE_CW: "Na šírku v smere hodinových ručičiek"
|
||||||
STR_INVERTED: "Obrátený"
|
STR_INVERTED: "Invertovaný"
|
||||||
|
STR_ORIENTATION_INVERTED: "Na výšku 180°"
|
||||||
STR_LANDSCAPE_CCW: "Na šírku proti smeru hodinových ručičiek"
|
STR_LANDSCAPE_CCW: "Na šírku proti smeru hodinových ručičiek"
|
||||||
STR_PREV_NEXT: "Predchádzajúca/Nasledujúca"
|
STR_PREV_NEXT: "Predchádzajúca/Nasledujúca"
|
||||||
STR_NEXT_PREV: "Nasledujúca/Predchádzajúca"
|
STR_NEXT_PREV: "Nasledujúca/Predchádzajúca"
|
||||||
|
|||||||
@@ -132,7 +132,8 @@ STR_SLEEP: "Spanje"
|
|||||||
STR_PAGE_TURN: "Obračanje strani"
|
STR_PAGE_TURN: "Obračanje strani"
|
||||||
STR_PORTRAIT: "Pokončno"
|
STR_PORTRAIT: "Pokončno"
|
||||||
STR_LANDSCAPE_CW: "Ležeče (v smeri urinega kazalca)"
|
STR_LANDSCAPE_CW: "Ležeče (v smeri urinega kazalca)"
|
||||||
STR_INVERTED: "Obrnjeno"
|
STR_INVERTED: "Invertirano"
|
||||||
|
STR_ORIENTATION_INVERTED: "Pokončno 180°"
|
||||||
STR_LANDSCAPE_CCW: "Ležeče (proti smeri urinega kazalca)"
|
STR_LANDSCAPE_CCW: "Ležeče (proti smeri urinega kazalca)"
|
||||||
STR_PREV_NEXT: "Nazaj/Naprej"
|
STR_PREV_NEXT: "Nazaj/Naprej"
|
||||||
STR_NEXT_PREV: "Naprej/Nazaj"
|
STR_NEXT_PREV: "Naprej/Nazaj"
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ STR_FORCE_REFRESH: "Refrescar pant."
|
|||||||
STR_PORTRAIT: "Vertical"
|
STR_PORTRAIT: "Vertical"
|
||||||
STR_LANDSCAPE_CW: "Horizontal (horario)"
|
STR_LANDSCAPE_CW: "Horizontal (horario)"
|
||||||
STR_INVERTED: "Invertido"
|
STR_INVERTED: "Invertido"
|
||||||
|
STR_ORIENTATION_INVERTED: "Al revés"
|
||||||
STR_LANDSCAPE_CCW: "Horizontal (antihorario)"
|
STR_LANDSCAPE_CCW: "Horizontal (antihorario)"
|
||||||
STR_PREV_NEXT: "Ant./Sig."
|
STR_PREV_NEXT: "Ant./Sig."
|
||||||
STR_NEXT_PREV: "Sig./Ant."
|
STR_NEXT_PREV: "Sig./Ant."
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ STR_FORCE_REFRESH: "Uppdatera skärmen"
|
|||||||
STR_PORTRAIT: "Porträtt"
|
STR_PORTRAIT: "Porträtt"
|
||||||
STR_LANDSCAPE_CW: "Landskap medurs"
|
STR_LANDSCAPE_CW: "Landskap medurs"
|
||||||
STR_INVERTED: "Inverterad"
|
STR_INVERTED: "Inverterad"
|
||||||
|
STR_ORIENTATION_INVERTED: "Porträtt 180°"
|
||||||
STR_LANDSCAPE_CCW: "Landskap moturs"
|
STR_LANDSCAPE_CCW: "Landskap moturs"
|
||||||
STR_PREV_NEXT: "Förra/Nästa"
|
STR_PREV_NEXT: "Förra/Nästa"
|
||||||
STR_NEXT_PREV: "Nästa/Förra"
|
STR_NEXT_PREV: "Nästa/Förra"
|
||||||
|
|||||||
@@ -130,7 +130,8 @@ STR_SLEEP: "Uyku"
|
|||||||
STR_PAGE_TURN: "Sayfa Çevirme"
|
STR_PAGE_TURN: "Sayfa Çevirme"
|
||||||
STR_PORTRAIT: "Dikey"
|
STR_PORTRAIT: "Dikey"
|
||||||
STR_LANDSCAPE_CW: "Yatay (Saat Yönü)"
|
STR_LANDSCAPE_CW: "Yatay (Saat Yönü)"
|
||||||
STR_INVERTED: "Ters"
|
STR_INVERTED: "Negatif"
|
||||||
|
STR_ORIENTATION_INVERTED: "Dikey 180°"
|
||||||
STR_LANDSCAPE_CCW: "Yatay (Saat Yönü Tersi)"
|
STR_LANDSCAPE_CCW: "Yatay (Saat Yönü Tersi)"
|
||||||
STR_PREV_NEXT: "Önceki/Sonraki"
|
STR_PREV_NEXT: "Önceki/Sonraki"
|
||||||
STR_NEXT_PREV: "Sonraki/Önceki"
|
STR_NEXT_PREV: "Sonraki/Önceki"
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ STR_PAGE_TURN: "Наст. сторінка"
|
|||||||
STR_FORCE_REFRESH: "Оновити екран"
|
STR_FORCE_REFRESH: "Оновити екран"
|
||||||
STR_PORTRAIT: "Книжкова"
|
STR_PORTRAIT: "Книжкова"
|
||||||
STR_LANDSCAPE_CW: "Альбом. за год."
|
STR_LANDSCAPE_CW: "Альбом. за год."
|
||||||
STR_INVERTED: "Перевернутий"
|
STR_INVERTED: "Інверсія"
|
||||||
|
STR_ORIENTATION_INVERTED: "Книжкова 180°"
|
||||||
STR_LANDSCAPE_CCW: "Альбом. проти год."
|
STR_LANDSCAPE_CCW: "Альбом. проти год."
|
||||||
STR_PREV_NEXT: "Попер/Наст"
|
STR_PREV_NEXT: "Попер/Наст"
|
||||||
STR_NEXT_PREV: "Наст/Попер"
|
STR_NEXT_PREV: "Наст/Попер"
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ STR_PAGE_TURN: "Canvi de pàgina"
|
|||||||
STR_PORTRAIT: "Vertical"
|
STR_PORTRAIT: "Vertical"
|
||||||
STR_LANDSCAPE_CW: "Horitzontal horari"
|
STR_LANDSCAPE_CW: "Horitzontal horari"
|
||||||
STR_INVERTED: "Invertit"
|
STR_INVERTED: "Invertit"
|
||||||
|
STR_ORIENTATION_INVERTED: "Vertical 180°"
|
||||||
STR_LANDSCAPE_CCW: "Horitzontal antihorari"
|
STR_LANDSCAPE_CCW: "Horitzontal antihorari"
|
||||||
STR_PREV_NEXT: "Anterior/Següent"
|
STR_PREV_NEXT: "Anterior/Següent"
|
||||||
STR_NEXT_PREV: "Següent/Anterior"
|
STR_NEXT_PREV: "Següent/Anterior"
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ STR_PAGE_TURN: "Lật trang"
|
|||||||
STR_FORCE_REFRESH: "Làm tươi màn hình"
|
STR_FORCE_REFRESH: "Làm tươi màn hình"
|
||||||
STR_PORTRAIT: "Dọc"
|
STR_PORTRAIT: "Dọc"
|
||||||
STR_LANDSCAPE_CW: "Ngang (thuận)"
|
STR_LANDSCAPE_CW: "Ngang (thuận)"
|
||||||
STR_INVERTED: "Lật ngược"
|
STR_INVERTED: "Đảo màu"
|
||||||
|
STR_ORIENTATION_INVERTED: "Dọc 180°"
|
||||||
STR_LANDSCAPE_CCW: "Ngang (ngược)"
|
STR_LANDSCAPE_CCW: "Ngang (ngược)"
|
||||||
STR_PREV_NEXT: "Trước/Sau"
|
STR_PREV_NEXT: "Trước/Sau"
|
||||||
STR_NEXT_PREV: "Sau/Trước"
|
STR_NEXT_PREV: "Sau/Trước"
|
||||||
|
|||||||
@@ -82,6 +82,17 @@ void HalDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* m
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HalDisplay::displayGrayscaleBase(RefreshMode fallback, bool turnOffScreen) {
|
void HalDisplay::displayGrayscaleBase(RefreshMode fallback, bool turnOffScreen) {
|
||||||
|
// X3: a HALF fallback means the caller wants a clean base (e.g. the sleep
|
||||||
|
// cover, a full-screen swap from arbitrary prior content). Without this, the
|
||||||
|
// X3 grayscale base takes its gentle differential happy path and the prior
|
||||||
|
// home/reader frame ghosts through the soft aa_pre_bw_mid waveform. Forcing a
|
||||||
|
// resync makes displayGrayscaleBase clear first, matching displayBuffer(HALF).
|
||||||
|
// The reader's FAST path is deliberately left on the differential path so
|
||||||
|
// per-page grayscale stays cheap.
|
||||||
|
if (gpio.deviceIsX3() && fallback == RefreshMode::HALF_REFRESH) {
|
||||||
|
einkDisplay.requestResync(1);
|
||||||
|
}
|
||||||
|
|
||||||
einkDisplay.displayGrayscaleBase(convertRefreshMode(fallback), turnOffScreen);
|
einkDisplay.displayGrayscaleBase(convertRefreshMode(fallback), turnOffScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class HalDisplay {
|
|||||||
void preconditionGrayscale(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
void preconditionGrayscale(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
||||||
|
|
||||||
// Display the framebuffer as the base frame for a grayscale overlay that
|
// Display the framebuffer as the base frame for a grayscale overlay that
|
||||||
// follows. X3 uses the OEM differential base waveform ("AA-pre-BW(mid)");
|
// follows. On X3, HALF fallback first requests a resync to match
|
||||||
// other panels display normally with `fallback` mode (previous behavior).
|
// displayBuffer(HALF); FAST fallback keeps the OEM differential base waveform
|
||||||
// Deliberately does NOT force the X3 resync that displayBuffer(HALF) does.
|
// ("AA-pre-BW(mid)"). Other panels display normally with `fallback` mode.
|
||||||
void displayGrayscaleBase(RefreshMode fallback = HALF_REFRESH, bool turnOffScreen = false);
|
void displayGrayscaleBase(RefreshMode fallback = HALF_REFRESH, bool turnOffScreen = false);
|
||||||
|
|
||||||
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
|
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build_cache_dir = .cache
|
|||||||
extra_configs = platformio.local.ini
|
extra_configs = platformio.local.ini
|
||||||
|
|
||||||
[crosspoint]
|
[crosspoint]
|
||||||
version = 1.4.0
|
version = 1.4.1
|
||||||
|
|
||||||
[base]
|
[base]
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.37/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.37/platform-espressif32.zip
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ class CrossPointSettings {
|
|||||||
XTC_STATUS_BAR_MODE_COUNT
|
XTC_STATUS_BAR_MODE_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum STATUS_BAR_CLOCK_MODE { STATUS_BAR_CLOCK_HIDE = 0, STATUS_BAR_CLOCK_RIGHT = 1, STATUS_BAR_CLOCK_LEFT = 2 };
|
||||||
|
|
||||||
enum ORIENTATION {
|
enum ORIENTATION {
|
||||||
PORTRAIT = 0, // 480x800 logical coordinates (current default)
|
PORTRAIT = 0, // 480x800 logical coordinates (current default)
|
||||||
LANDSCAPE_CW = 1, // 800x480 logical coordinates, rotated 180° (swap top/bottom)
|
LANDSCAPE_CW = 1, // 800x480 logical coordinates, rotated 180° (swap top/bottom)
|
||||||
@@ -188,7 +190,7 @@ class CrossPointSettings {
|
|||||||
uint8_t statusBarBattery = 1;
|
uint8_t statusBarBattery = 1;
|
||||||
uint8_t xtcStatusBarMode = XTC_STATUS_BAR_HIDE;
|
uint8_t xtcStatusBarMode = XTC_STATUS_BAR_HIDE;
|
||||||
// Clock display in status bar (X3 only, requires DS3231 RTC)
|
// Clock display in status bar (X3 only, requires DS3231 RTC)
|
||||||
uint8_t statusBarClock = 0;
|
uint8_t statusBarClock = STATUS_BAR_CLOCK_HIDE;
|
||||||
// Clock UTC offset in quarter-hour steps, biased by 48 so it fits in uint8_t.
|
// Clock UTC offset in quarter-hour steps, biased by 48 so it fits in uint8_t.
|
||||||
// Value 48 = UTC+0, 0 = UTC-12:00, 104 = UTC+14:00.
|
// Value 48 = UTC+0, 0 = UTC-12:00, 104 = UTC+14:00.
|
||||||
// Quarter-hour granularity supports oddball zones like Nepal (+5:45) and Chatham (+12:45).
|
// Quarter-hour granularity supports oddball zones like Nepal (+5:45) and Chatham (+12:45).
|
||||||
|
|||||||
+7
-5
@@ -151,9 +151,10 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
|
|||||||
StrId::STR_CAT_READER),
|
StrId::STR_CAT_READER),
|
||||||
SettingInfo::Toggle(StrId::STR_HYPHENATION, &CrossPointSettings::hyphenationEnabled, "hyphenationEnabled",
|
SettingInfo::Toggle(StrId::STR_HYPHENATION, &CrossPointSettings::hyphenationEnabled, "hyphenationEnabled",
|
||||||
StrId::STR_CAT_READER),
|
StrId::STR_CAT_READER),
|
||||||
SettingInfo::Enum(StrId::STR_ORIENTATION, &CrossPointSettings::orientation,
|
SettingInfo::Enum(
|
||||||
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW},
|
StrId::STR_ORIENTATION, &CrossPointSettings::orientation,
|
||||||
"orientation", StrId::STR_CAT_READER),
|
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_ORIENTATION_INVERTED, StrId::STR_LANDSCAPE_CCW},
|
||||||
|
"orientation", StrId::STR_CAT_READER),
|
||||||
SettingInfo::Toggle(StrId::STR_EXTRA_SPACING, &CrossPointSettings::extraParagraphSpacing,
|
SettingInfo::Toggle(StrId::STR_EXTRA_SPACING, &CrossPointSettings::extraParagraphSpacing,
|
||||||
"extraParagraphSpacing", StrId::STR_CAT_READER),
|
"extraParagraphSpacing", StrId::STR_CAT_READER),
|
||||||
SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
|
SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
|
||||||
@@ -244,8 +245,9 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
|
|||||||
StrId::STR_CUSTOMISE_STATUS_BAR),
|
StrId::STR_CUSTOMISE_STATUS_BAR),
|
||||||
// Clock entries (web settings only; device UI uses ClockOffsetActivity for the offset).
|
// Clock entries (web settings only; device UI uses ClockOffsetActivity for the offset).
|
||||||
// Range 0..104 = quarter-hour steps from UTC-12:00 to UTC+14:00, biased by 48.
|
// Range 0..104 = quarter-hour steps from UTC-12:00 to UTC+14:00, biased by 48.
|
||||||
SettingInfo::Toggle(StrId::STR_CLOCK, &CrossPointSettings::statusBarClock, "statusBarClock",
|
SettingInfo::Enum(StrId::STR_CLOCK, &CrossPointSettings::statusBarClock,
|
||||||
StrId::STR_CUSTOMISE_STATUS_BAR),
|
{StrId::STR_HIDE, StrId::STR_DIR_LEFT, StrId::STR_DIR_RIGHT}, "statusBarClock",
|
||||||
|
StrId::STR_CUSTOMISE_STATUS_BAR),
|
||||||
SettingInfo::Value(StrId::STR_CLOCK_UTC_OFFSET, &CrossPointSettings::clockUtcOffsetQ, {0, 104, 1},
|
SettingInfo::Value(StrId::STR_CLOCK_UTC_OFFSET, &CrossPointSettings::clockUtcOffsetQ, {0, 104, 1},
|
||||||
"clockUtcOffsetQ", StrId::STR_CUSTOMISE_STATUS_BAR),
|
"clockUtcOffsetQ", StrId::STR_CUSTOMISE_STATUS_BAR),
|
||||||
SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat,
|
SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat,
|
||||||
|
|||||||
@@ -83,9 +83,10 @@ ProgressRange getPageProgressRange(const std::shared_ptr<Epub>& epub, const int
|
|||||||
return {epub->calculateProgress(spineIndex, start), epub->calculateProgress(spineIndex, end)};
|
return {epub->calculateProgress(spineIndex, start), epub->calculateProgress(spineIndex, end)};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bookmarkMatchesProgress(const BookmarkEntry& bookmark, const SavedProgressPosition& progress,
|
bool bookmarkMatchesProgress(const BookmarkEntry& bookmark, const int spineIndex, const int page, const int pageCount,
|
||||||
const ProgressRange& pageRange) {
|
const ProgressRange& pageRange) {
|
||||||
if (bookmark.xpath == progress.xpath) {
|
if (bookmark.computedSpineIndex == spineIndex && bookmark.computedChapterPageCount == pageCount &&
|
||||||
|
bookmark.computedChapterProgress == page) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1311,10 +1312,12 @@ void EpubReaderActivity::addBookmark() {
|
|||||||
const ProgressRange pageRange = getPageProgressRange(epub, currentSpineIndex, currentPage, pageCount);
|
const ProgressRange pageRange = getPageProgressRange(epub, currentSpineIndex, currentPage, pageCount);
|
||||||
|
|
||||||
const size_t bookmarkCountBeforeToggle = cachedBookmarks.size();
|
const size_t bookmarkCountBeforeToggle = cachedBookmarks.size();
|
||||||
cachedBookmarks.erase(
|
cachedBookmarks.erase(std::remove_if(cachedBookmarks.begin(), cachedBookmarks.end(),
|
||||||
std::remove_if(cachedBookmarks.begin(), cachedBookmarks.end(),
|
[&](const BookmarkEntry& b) {
|
||||||
[&](const BookmarkEntry& b) { return bookmarkMatchesProgress(b, progress, pageRange); }),
|
return bookmarkMatchesProgress(b, currentSpineIndex, currentPage, pageCount,
|
||||||
cachedBookmarks.end());
|
pageRange);
|
||||||
|
}),
|
||||||
|
cachedBookmarks.end());
|
||||||
if (cachedBookmarks.size() != bookmarkCountBeforeToggle) {
|
if (cachedBookmarks.size() != bookmarkCountBeforeToggle) {
|
||||||
bookmarkRemoved = true;
|
bookmarkRemoved = true;
|
||||||
currentPageBookmarked = false;
|
currentPageBookmarked = false;
|
||||||
@@ -1350,11 +1353,10 @@ void EpubReaderActivity::updateBookmarkFlag() {
|
|||||||
currentPageBookmarked = false;
|
currentPageBookmarked = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SavedProgressPosition progress = ProgressMapper::toSavedProgress(epub, getCurrentPosition());
|
|
||||||
const ProgressRange pageRange =
|
const ProgressRange pageRange =
|
||||||
getPageProgressRange(epub, currentSpineIndex, section->currentPage, section->pageCount);
|
getPageProgressRange(epub, currentSpineIndex, section->currentPage, section->pageCount);
|
||||||
currentPageBookmarked = std::any_of(cachedBookmarks.begin(), cachedBookmarks.end(), [&](const BookmarkEntry& b) {
|
currentPageBookmarked = std::any_of(cachedBookmarks.begin(), cachedBookmarks.end(), [&](const BookmarkEntry& b) {
|
||||||
return bookmarkMatchesProgress(b, progress, pageRange);
|
return bookmarkMatchesProgress(b, currentSpineIndex, section->currentPage, section->pageCount, pageRange);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ const StrId titleNames[TITLE_ITEMS] = {StrId::STR_BOOK, StrId::STR_CHAPTER, StrI
|
|||||||
constexpr int XTC_STATUS_BAR_ITEMS = 3;
|
constexpr int XTC_STATUS_BAR_ITEMS = 3;
|
||||||
const StrId xtcStatusBarNames[XTC_STATUS_BAR_ITEMS] = {StrId::STR_HIDE, StrId::STR_BOTTOM, StrId::STR_TOP};
|
const StrId xtcStatusBarNames[XTC_STATUS_BAR_ITEMS] = {StrId::STR_HIDE, StrId::STR_BOTTOM, StrId::STR_TOP};
|
||||||
|
|
||||||
|
constexpr int STATUS_BAR_CLOCK_ITEMS = 3;
|
||||||
|
const StrId statusBarClockNames[STATUS_BAR_CLOCK_ITEMS] = {StrId::STR_HIDE, StrId::STR_DIR_RIGHT, StrId::STR_DIR_LEFT};
|
||||||
|
|
||||||
const int verticalPreviewPadding = 50;
|
const int verticalPreviewPadding = 50;
|
||||||
const int verticalPreviewTextPadding = 40;
|
const int verticalPreviewTextPadding = 40;
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -112,6 +115,10 @@ void StatusBarSettingsActivity::onEnter() {
|
|||||||
SETTINGS.clockFormat = 0;
|
SETTINGS.clockFormat = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SETTINGS.statusBarClock >= STATUS_BAR_CLOCK_ITEMS) {
|
||||||
|
SETTINGS.statusBarClock = CrossPointSettings::STATUS_BAR_CLOCK_MODE::STATUS_BAR_CLOCK_HIDE;
|
||||||
|
}
|
||||||
|
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +183,7 @@ void StatusBarSettingsActivity::handleSelection() {
|
|||||||
SETTINGS.xtcStatusBarMode = (SETTINGS.xtcStatusBarMode + 1) % XTC_STATUS_BAR_ITEMS;
|
SETTINGS.xtcStatusBarMode = (SETTINGS.xtcStatusBarMode + 1) % XTC_STATUS_BAR_ITEMS;
|
||||||
break;
|
break;
|
||||||
case ITEM_CLOCK:
|
case ITEM_CLOCK:
|
||||||
SETTINGS.statusBarClock = (SETTINGS.statusBarClock + 1) % 2;
|
SETTINGS.statusBarClock = (SETTINGS.statusBarClock + 1) % STATUS_BAR_CLOCK_ITEMS;
|
||||||
break;
|
break;
|
||||||
case ITEM_CLOCK_FORMAT:
|
case ITEM_CLOCK_FORMAT:
|
||||||
SETTINGS.clockFormat = (SETTINGS.clockFormat + 1) % CLOCK_FORMAT_ITEMS;
|
SETTINGS.clockFormat = (SETTINGS.clockFormat + 1) % CLOCK_FORMAT_ITEMS;
|
||||||
@@ -225,7 +232,7 @@ void StatusBarSettingsActivity::render(RenderLock&&) {
|
|||||||
case ITEM_XTC_STATUS_BAR:
|
case ITEM_XTC_STATUS_BAR:
|
||||||
return I18N.get(xtcStatusBarNames[SETTINGS.xtcStatusBarMode]);
|
return I18N.get(xtcStatusBarNames[SETTINGS.xtcStatusBarMode]);
|
||||||
case ITEM_CLOCK:
|
case ITEM_CLOCK:
|
||||||
return SETTINGS.statusBarClock ? tr(STR_SHOW) : tr(STR_HIDE);
|
return I18N.get(statusBarClockNames[SETTINGS.statusBarClock]);
|
||||||
case ITEM_CLOCK_FORMAT: {
|
case ITEM_CLOCK_FORMAT: {
|
||||||
const uint8_t fmt = SETTINGS.clockFormat < CLOCK_FORMAT_ITEMS ? SETTINGS.clockFormat : 0;
|
const uint8_t fmt = SETTINGS.clockFormat < CLOCK_FORMAT_ITEMS ? SETTINGS.clockFormat : 0;
|
||||||
return std::string(I18N.get(clockFormatNames[fmt]));
|
return std::string(I18N.get(clockFormatNames[fmt]));
|
||||||
|
|||||||
@@ -131,9 +131,10 @@ int UITheme::getStatusBarHeight() {
|
|||||||
const ThemeMetrics& metrics = UITheme::getInstance().getMetrics();
|
const ThemeMetrics& metrics = UITheme::getInstance().getMetrics();
|
||||||
|
|
||||||
// Add status bar margin
|
// Add status bar margin
|
||||||
const bool showStatusBar = SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage ||
|
const bool showStatusBar =
|
||||||
SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE ||
|
SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage ||
|
||||||
SETTINGS.statusBarBattery;
|
SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE || SETTINGS.statusBarBattery ||
|
||||||
|
SETTINGS.statusBarClock != CrossPointSettings::STATUS_BAR_CLOCK_MODE::STATUS_BAR_CLOCK_HIDE;
|
||||||
const bool showProgressBar =
|
const bool showProgressBar =
|
||||||
SETTINGS.statusBarProgressBar != CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS;
|
SETTINGS.statusBarProgressBar != CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS;
|
||||||
return (showStatusBar ? (metrics.statusBarVerticalMargin) : 0) +
|
return (showStatusBar ? (metrics.statusBarVerticalMargin) : 0) +
|
||||||
|
|||||||
@@ -759,7 +759,11 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c
|
|||||||
// Draw Progress Text
|
// Draw Progress Text
|
||||||
const auto screenHeight = renderer.getScreenHeight();
|
const auto screenHeight = renderer.getScreenHeight();
|
||||||
auto textY = screenHeight - UITheme::getInstance().getStatusBarHeight() - orientedMarginBottom - paddingBottom - 4;
|
auto textY = screenHeight - UITheme::getInstance().getStatusBarHeight() - orientedMarginBottom - paddingBottom - 4;
|
||||||
int progressTextWidth = 0;
|
|
||||||
|
const int leftClusterX = metrics.statusBarHorizontalMargin + orientedMarginLeft + 1;
|
||||||
|
const int rightClusterX = renderer.getScreenWidth() - metrics.statusBarHorizontalMargin - orientedMarginRight;
|
||||||
|
int leftClusterWidth = 0;
|
||||||
|
int rightClusterWidth = 0;
|
||||||
|
|
||||||
if (SETTINGS.statusBarBookProgressPercentage || SETTINGS.statusBarChapterPageCount) {
|
if (SETTINGS.statusBarBookProgressPercentage || SETTINGS.statusBarChapterPageCount) {
|
||||||
// Right aligned text for progress counter
|
// Right aligned text for progress counter
|
||||||
@@ -773,11 +777,10 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c
|
|||||||
snprintf(progressStr, sizeof(progressStr), "%d/%d", currentPage, pageCount);
|
snprintf(progressStr, sizeof(progressStr), "%d/%d", currentPage, pageCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progressStr);
|
int progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progressStr);
|
||||||
renderer.drawText(
|
renderer.drawText(SMALL_FONT_ID, rightClusterX - progressTextWidth, textY, progressStr);
|
||||||
SMALL_FONT_ID,
|
|
||||||
renderer.getScreenWidth() - metrics.statusBarHorizontalMargin - orientedMarginRight - progressTextWidth, textY,
|
rightClusterWidth += progressTextWidth;
|
||||||
progressStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw Progress Bar
|
// Draw Progress Bar
|
||||||
@@ -800,39 +803,53 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c
|
|||||||
renderer.fillRect(barMarginLeft, progressBarY, barWidth, barHeight, true);
|
renderer.fillRect(barMarginLeft, progressBarY, barWidth, barHeight, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw Bookmark
|
|
||||||
const int leftClusterX = metrics.statusBarHorizontalMargin + orientedMarginLeft + 1;
|
|
||||||
const bool showBookmarkIcon = showStatusBarTextLane && isPageBookmarked;
|
|
||||||
const int bookmarkReserveWidth = showBookmarkIcon ? (bookmarkStatusIconWidth + bookmarkStatusIconGap) : 0;
|
|
||||||
if (showBookmarkIcon) {
|
|
||||||
const int bookmarkY = textY + 5;
|
|
||||||
drawBookmarkStatusIcon(renderer, leftClusterX, bookmarkY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw Battery
|
// Draw Battery
|
||||||
const bool showBatteryPercentage =
|
const bool showBatteryPercentage =
|
||||||
SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER;
|
SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER;
|
||||||
int leftClusterWidth = bookmarkReserveWidth;
|
|
||||||
if (SETTINGS.statusBarBattery) {
|
if (SETTINGS.statusBarBattery) {
|
||||||
GUI.drawBatteryLeft(renderer,
|
GUI.drawBatteryLeft(renderer,
|
||||||
Rect{leftClusterX + bookmarkReserveWidth, textY, metrics.batteryWidth, metrics.batteryHeight},
|
Rect{leftClusterX + leftClusterWidth, textY, metrics.batteryWidth, metrics.batteryHeight},
|
||||||
showBatteryPercentage);
|
showBatteryPercentage);
|
||||||
leftClusterWidth += showBatteryPercentage ? 50 : 20;
|
int batteryWidth = metrics.batteryWidth;
|
||||||
|
|
||||||
|
if (showBatteryPercentage) {
|
||||||
|
const uint16_t percentage = powerManager.getBatteryPercentage();
|
||||||
|
// width of icon + spacing + text for layout purposes
|
||||||
|
batteryWidth +=
|
||||||
|
batteryPercentSpacing + renderer.getTextWidth(SMALL_FONT_ID, (std::to_string(percentage) + "%").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
leftClusterWidth += batteryWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw Clock (X3 only — DS3231 RTC)
|
// Draw Clock (X3 only — DS3231 RTC)
|
||||||
int clockTextWidth = 0;
|
|
||||||
if (SETTINGS.statusBarClock && halClock.isAvailable()) {
|
if (SETTINGS.statusBarClock && halClock.isAvailable()) {
|
||||||
char timeBuf[9];
|
char timeBuf[9];
|
||||||
if (halClock.formatTime(timeBuf, sizeof(timeBuf), SETTINGS.clockUtcOffsetQ, SETTINGS.clockFormat == 1)) {
|
if (halClock.formatTime(timeBuf, sizeof(timeBuf), SETTINGS.clockUtcOffsetQ, SETTINGS.clockFormat == 1)) {
|
||||||
clockTextWidth = renderer.getTextWidth(SMALL_FONT_ID, timeBuf);
|
int clockTextWidth = renderer.getTextWidth(SMALL_FONT_ID, timeBuf);
|
||||||
// Position to the left of the progress text (with a small gap)
|
int clockX = 0;
|
||||||
const int clockX = renderer.getScreenWidth() - metrics.statusBarHorizontalMargin - orientedMarginRight -
|
// Position to the left or right of the progress text (with a small gap)
|
||||||
progressTextWidth - (progressTextWidth > 0 ? 10 : 0) - clockTextWidth;
|
if (SETTINGS.statusBarClock == CrossPointSettings::STATUS_BAR_CLOCK_LEFT) {
|
||||||
|
clockX = leftClusterX + leftClusterWidth + (leftClusterWidth > 0 ? 10 : 0);
|
||||||
|
leftClusterWidth += clockTextWidth + 10;
|
||||||
|
} else if (SETTINGS.statusBarClock == CrossPointSettings::STATUS_BAR_CLOCK_RIGHT) {
|
||||||
|
clockX = rightClusterX - rightClusterWidth - (rightClusterWidth > 0 ? 10 : 0) - clockTextWidth;
|
||||||
|
rightClusterWidth += clockTextWidth + 10;
|
||||||
|
}
|
||||||
renderer.drawText(SMALL_FONT_ID, clockX, textY, timeBuf);
|
renderer.drawText(SMALL_FONT_ID, clockX, textY, timeBuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw Bookmark
|
||||||
|
if (showStatusBarTextLane && isPageBookmarked) {
|
||||||
|
const int bookmarkGap = leftClusterWidth > 0 ? bookmarkStatusIconGap : 0;
|
||||||
|
const int bookmarkX = leftClusterX + leftClusterWidth + bookmarkGap;
|
||||||
|
const int bookmarkY = textY + 5;
|
||||||
|
drawBookmarkStatusIcon(renderer, bookmarkX, bookmarkY);
|
||||||
|
leftClusterWidth += bookmarkStatusIconWidth + bookmarkGap;
|
||||||
|
}
|
||||||
|
|
||||||
// Draw Title
|
// Draw Title
|
||||||
if (!title.empty()) {
|
if (!title.empty()) {
|
||||||
textY -= textYOffset;
|
textY -= textYOffset;
|
||||||
@@ -842,8 +859,7 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c
|
|||||||
renderer.getScreenWidth() - (metrics.statusBarHorizontalMargin * 2) - orientedMarginLeft - orientedMarginRight;
|
renderer.getScreenWidth() - (metrics.statusBarHorizontalMargin * 2) - orientedMarginLeft - orientedMarginRight;
|
||||||
|
|
||||||
const int titleMarginLeft = leftClusterWidth + 30;
|
const int titleMarginLeft = leftClusterWidth + 30;
|
||||||
const int clockReserve = clockTextWidth > 0 ? (clockTextWidth + 10) : 0;
|
const int titleMarginRight = rightClusterWidth + 30;
|
||||||
const int titleMarginRight = progressTextWidth + clockReserve + 30;
|
|
||||||
|
|
||||||
// Attempt to center title on the screen, but if title is too wide then later we will center it within the
|
// Attempt to center title on the screen, but if title is too wide then later we will center it within the
|
||||||
// available space.
|
// available space.
|
||||||
|
|||||||
@@ -80,11 +80,11 @@
|
|||||||
}
|
}
|
||||||
.breadcrumb-inline .sep {
|
.breadcrumb-inline .sep {
|
||||||
margin: 0 6px;
|
margin: 0 6px;
|
||||||
color: var(--border-color);
|
color: var(--label-color);
|
||||||
}
|
}
|
||||||
.breadcrumb-inline .current {
|
.breadcrumb-inline .current {
|
||||||
color: var(--title-color);
|
color: var(--title-color);
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.nav-links {
|
.nav-links {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
@@ -960,12 +960,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
.contents-title {
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--title-color);
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.summary-inline {
|
.summary-inline {
|
||||||
color: var(--label-color);
|
color: var(--label-color);
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
@@ -1296,9 +1290,6 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
.contents-title {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
.summary-inline {
|
.summary-inline {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
@@ -1501,7 +1492,6 @@
|
|||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="page-header-left">
|
<div class="page-header-left">
|
||||||
<h2>📁 File Manager</h2>
|
<h2>📁 File Manager</h2>
|
||||||
<div class="breadcrumb-inline" id="directory-breadcrumbs"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@@ -1523,7 +1513,7 @@
|
|||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="contents-header">
|
<div class="contents-header">
|
||||||
<h2 class="contents-title">Contents</h2>
|
<div class="breadcrumb-inline" id="directory-breadcrumbs"></div>
|
||||||
<span class="summary-inline" id="folder-summary"></span>
|
<span class="summary-inline" id="folder-summary"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1904,19 +1894,44 @@
|
|||||||
const breadcrumbs = document.getElementById('directory-breadcrumbs');
|
const breadcrumbs = document.getElementById('directory-breadcrumbs');
|
||||||
const fileTable = document.getElementById('file-table');
|
const fileTable = document.getElementById('file-table');
|
||||||
|
|
||||||
let breadcrumbContent = '<span class="sep">/</span>';
|
const segments = currentPath.split('/').filter(Boolean);
|
||||||
if (currentPath === '/') {
|
breadcrumbs.replaceChildren();
|
||||||
breadcrumbContent += '<span class="current">🏠</span>';
|
|
||||||
|
const appendSep = function() {
|
||||||
|
const sep = document.createElement('span');
|
||||||
|
sep.className = 'sep';
|
||||||
|
sep.textContent = '›';
|
||||||
|
breadcrumbs.appendChild(sep);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendLink = function(label, href) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = href;
|
||||||
|
link.textContent = label;
|
||||||
|
breadcrumbs.appendChild(link);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendCurrent = function(label) {
|
||||||
|
const current = document.createElement('span');
|
||||||
|
current.className = 'current';
|
||||||
|
current.textContent = label;
|
||||||
|
breadcrumbs.appendChild(current);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (segments.length === 0) {
|
||||||
|
appendCurrent('🏠 Home');
|
||||||
} else {
|
} else {
|
||||||
breadcrumbContent += '<a href="/files">🏠</a>';
|
appendLink('🏠 Home', '/files');
|
||||||
const pathSegments = currentPath.split('/');
|
segments.forEach(function(segment, index) {
|
||||||
pathSegments.slice(1, pathSegments.length - 1).forEach(function(segment, index) {
|
appendSep();
|
||||||
breadcrumbContent += '<span class="sep">/</span><a href="/files?path=' + encodeURIComponent(pathSegments.slice(0, index + 2).join('/')) + '">' + escapeHtml(segment) + '</a>';
|
if (index === segments.length - 1) {
|
||||||
|
appendCurrent(segment);
|
||||||
|
} else {
|
||||||
|
const path = '/' + segments.slice(0, index + 1).join('/');
|
||||||
|
appendLink(segment, '/files?path=' + encodeURIComponent(path));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
breadcrumbContent += '<span class="sep">/</span>';
|
|
||||||
breadcrumbContent += '<span class="current">' + escapeHtml(pathSegments[pathSegments.length - 1]) + '</span>';
|
|
||||||
}
|
}
|
||||||
breadcrumbs.innerHTML = breadcrumbContent;
|
|
||||||
|
|
||||||
let files = [];
|
let files = [];
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user