summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--res/fonts/SmolEmoji-Regular.ttfbin51452 -> 57296 bytes
-rw-r--r--src/app.c41
-rw-r--r--src/gmdocument.c12
-rw-r--r--src/ui/documentwidget.c7
-rw-r--r--src/ui/text.c28
-rw-r--r--src/ui/text.h3
7 files changed, 85 insertions, 11 deletions
diff --git a/README.md b/README.md
index f1a62578..5838d798 100644
--- a/README.md
+++ b/README.md
@@ -21,11 +21,10 @@ Like Gemini, Lagrange has been designed with minimalism in mind. It depends on a
21 21
22Prebuilt binaries for Windows, macOS and Linux can be found in [Releases][rel]. You can also find [Lagrange on Flathub for Linux](https://flathub.org/apps/details/fi.skyjake.Lagrange). 22Prebuilt binaries for Windows, macOS and Linux can be found in [Releases][rel]. You can also find [Lagrange on Flathub for Linux](https://flathub.org/apps/details/fi.skyjake.Lagrange).
23 23
24On macOS you can install and upgrade via a Homebrew tap: 24On macOS you can install and upgrade via Homebrew:
25 25
26``` 26```
27$ brew tap skyjake/lagrange 27brew install --cask lagrange
28$ brew install lagrange
29``` 28```
30 29
31On openSUSE Tumbleweed: 30On openSUSE Tumbleweed:
diff --git a/res/fonts/SmolEmoji-Regular.ttf b/res/fonts/SmolEmoji-Regular.ttf
index 26810648..5ef05e27 100644
--- a/res/fonts/SmolEmoji-Regular.ttf
+++ b/res/fonts/SmolEmoji-Regular.ttf
Binary files differ
diff --git a/src/app.c b/src/app.c
index 3a1ee101..b25a213e 100644
--- a/src/app.c
+++ b/src/app.c
@@ -59,6 +59,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
59#include <stdarg.h> 59#include <stdarg.h>
60#include <errno.h> 60#include <errno.h>
61 61
62//#define LAGRANGE_ENABLE_MOUSE_TOUCH_EMULATION 1
63
62#if defined (iPlatformAppleDesktop) 64#if defined (iPlatformAppleDesktop)
63# include "macos.h" 65# include "macos.h"
64#endif 66#endif
@@ -1197,6 +1199,45 @@ void processEvents_App(enum iAppEventMode eventMode) {
1197 ev.wheel.x = -ev.wheel.x; 1199 ev.wheel.x = -ev.wheel.x;
1198#endif 1200#endif
1199 } 1201 }
1202#if defined (LAGRANGE_ENABLE_MOUSE_TOUCH_EMULATION)
1203 /* Convert mouse events to finger events to test the touch handling. */ {
1204 static float xPrev = 0.0f;
1205 static float yPrev = 0.0f;
1206 if (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP) {
1207 const float xf = (d->window->pixelRatio * ev.button.x) / (float) d->window->size.x;
1208 const float yf = (d->window->pixelRatio * ev.button.y) / (float) d->window->size.y;
1209 ev.type = (ev.type == SDL_MOUSEBUTTONDOWN ? SDL_FINGERDOWN : SDL_FINGERUP);
1210 ev.tfinger.x = xf;
1211 ev.tfinger.y = yf;
1212 ev.tfinger.dx = xf - xPrev;
1213 ev.tfinger.dy = yf - yPrev;
1214 xPrev = xf;
1215 yPrev = yf;
1216 ev.tfinger.fingerId = 0x1234;
1217 ev.tfinger.pressure = 1.0f;
1218 ev.tfinger.timestamp = SDL_GetTicks();
1219 ev.tfinger.touchId = SDL_TOUCH_MOUSEID;
1220 }
1221 else if (ev.type == SDL_MOUSEMOTION) {
1222 if (~ev.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT)) {
1223 continue; /* only when pressing a button */
1224 }
1225 const float xf = (d->window->pixelRatio * ev.motion.x) / (float) d->window->size.x;
1226 const float yf = (d->window->pixelRatio * ev.motion.y) / (float) d->window->size.y;
1227 ev.type = SDL_FINGERMOTION;
1228 ev.tfinger.x = xf;
1229 ev.tfinger.y = yf;
1230 ev.tfinger.dx = xf - xPrev;
1231 ev.tfinger.dy = yf - yPrev;
1232 xPrev = xf;
1233 yPrev = yf;
1234 ev.tfinger.fingerId = 0x1234;
1235 ev.tfinger.pressure = 1.0f;
1236 ev.tfinger.timestamp = SDL_GetTicks();
1237 ev.tfinger.touchId = SDL_TOUCH_MOUSEID;
1238 }
1239 }
1240#endif
1200 iBool wasUsed = processEvent_Window(d->window, &ev); 1241 iBool wasUsed = processEvent_Window(d->window, &ev);
1201 if (!wasUsed) { 1242 if (!wasUsed) {
1202 /* There may be a key bindings for this. */ 1243 /* There may be a key bindings for this. */
diff --git a/src/gmdocument.c b/src/gmdocument.c
index aa2baa4a..f15d9d1d 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -289,6 +289,12 @@ static void clearLinks_GmDocument_(iGmDocument *d) {
289 clear_PtrArray(&d->links); 289 clear_PtrArray(&d->links);
290} 290}
291 291
292static iBool isGopher_GmDocument_(const iGmDocument *d) {
293 const iRangecc scheme = urlScheme_String(&d->url);
294 return (equalCase_Rangecc(scheme, "gopher") ||
295 equalCase_Rangecc(scheme, "finger"));
296}
297
292static iBool isForcedMonospace_GmDocument_(const iGmDocument *d) { 298static iBool isForcedMonospace_GmDocument_(const iGmDocument *d) {
293 const iRangecc scheme = urlScheme_String(&d->url); 299 const iRangecc scheme = urlScheme_String(&d->url);
294 if (equalCase_Rangecc(scheme, "gemini")) { 300 if (equalCase_Rangecc(scheme, "gemini")) {
@@ -364,6 +370,7 @@ static void updateOpenURLs_GmDocument_(iGmDocument *d) {
364static void doLayout_GmDocument_(iGmDocument *d) { 370static void doLayout_GmDocument_(iGmDocument *d) {
365 const iPrefs *prefs = prefs_App(); 371 const iPrefs *prefs = prefs_App();
366 const iBool isMono = isForcedMonospace_GmDocument_(d); 372 const iBool isMono = isForcedMonospace_GmDocument_(d);
373 const iBool isGopher = isGopher_GmDocument_(d);
367 const iBool isNarrow = d->size.x < 90 * gap_Text; 374 const iBool isNarrow = d->size.x < 90 * gap_Text;
368 const iBool isVeryNarrow = d->size.x <= 70 * gap_Text; 375 const iBool isVeryNarrow = d->size.x <= 70 * gap_Text;
369 const iBool isExtremelyNarrow = d->size.x <= 60 * gap_Text; 376 const iBool isExtremelyNarrow = d->size.x <= 60 * gap_Text;
@@ -482,7 +489,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
482 meta.flags = constValue_Array(oldPreMeta, preIndex, iGmPreMeta).flags & 489 meta.flags = constValue_Array(oldPreMeta, preIndex, iGmPreMeta).flags &
483 folded_GmPreMetaFlag; 490 folded_GmPreMetaFlag;
484 } 491 }
485 else if (prefs->collapsePreOnLoad) { 492 else if (prefs->collapsePreOnLoad && !isGopher) {
486 meta.flags |= folded_GmPreMetaFlag; 493 meta.flags |= folded_GmPreMetaFlag;
487 } 494 }
488 pushBack_Array(&d->preMeta, &meta); 495 pushBack_Array(&d->preMeta, &meta);
@@ -1451,6 +1458,9 @@ iBool updateOpenURLs_GmDocument(iGmDocument *d) {
1451 const iBool isOpen = contains_StringSet(d->openURLs, &link->url); 1458 const iBool isOpen = contains_StringSet(d->openURLs, &link->url);
1452 if (isOpen ^ ((link->flags & isOpen_GmLinkFlag) != 0)) { 1459 if (isOpen ^ ((link->flags & isOpen_GmLinkFlag) != 0)) {
1453 iChangeFlags(link->flags, isOpen_GmLinkFlag, isOpen); 1460 iChangeFlags(link->flags, isOpen_GmLinkFlag, isOpen);
1461 if (isOpen) {
1462 link->flags |= visited_GmLinkFlag;
1463 }
1454 wasChanged = iTrue; 1464 wasChanged = iTrue;
1455 } 1465 }
1456 } 1466 }
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 7f8d70a3..cb1fde28 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -4155,11 +4155,12 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4155 linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); 4155 linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase);
4156 if (ordChar) { 4156 if (ordChar) {
4157 const char *circle = "\u25ef"; /* Large Circle */ 4157 const char *circle = "\u25ef"; /* Large Circle */
4158 const int circleFont = defaultContentRegular_FontId;
4158 iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), 4159 iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y),
4159 init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(run->font)) }; 4160 init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) };
4160 drawRange_Text( 4161 drawRange_Text(
4161 run->font, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); 4162 circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle));
4162 iRect circleArea = visualBounds_Text(run->font, range_CStr(circle)); 4163 iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle));
4163 addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); 4164 addv_I2(&circleArea.pos, topLeft_Rect(nbArea));
4164 drawCentered_Text(defaultContentSmall_FontId, 4165 drawCentered_Text(defaultContentSmall_FontId,
4165 circleArea, 4166 circleArea,
diff --git a/src/ui/text.c b/src/ui/text.c
index ae1248a7..edbc6583 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -116,6 +116,7 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch)
116 116
117struct Impl_Font { 117struct Impl_Font {
118 iBlock * data; 118 iBlock * data;
119 enum iTextFont family;
119 stbtt_fontinfo font; 120 stbtt_fontinfo font;
120 float xScale, yScale; 121 float xScale, yScale;
121 int vertOffset; /* offset due to scaling */ 122 int vertOffset; /* offset due to scaling */
@@ -134,6 +135,15 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale,
134 enum iFontSize sizeId, iBool isMonospaced) { 135 enum iFontSize sizeId, iBool isMonospaced) {
135 init_Hash(&d->glyphs); 136 init_Hash(&d->glyphs);
136 d->data = NULL; 137 d->data = NULL;
138 d->family = undefined_TextFont;
139 /* Note: We only use `family` currently for applying a kerning fix to Nunito. */
140 if (data == &fontNunitoRegular_Embedded ||
141 data == &fontNunitoBold_Embedded ||
142 data == &fontNunitoExtraBold_Embedded ||
143 data == &fontNunitoLightItalic_Embedded ||
144 data == &fontNunitoExtraLight_Embedded) {
145 d->family = nunito_TextFont;
146 }
137 d->isMonospaced = isMonospaced; 147 d->isMonospaced = isMonospaced;
138 d->height = height; 148 d->height = height;
139 iZap(d->font); 149 iZap(d->font);
@@ -1131,14 +1141,26 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1131 const iChar next = nextChar_(&peek, args->text.end); 1141 const iChar next = nextChar_(&peek, args->text.end);
1132 if (enableKerning_Text && !d->manualKernOnly && next) { 1142 if (enableKerning_Text && !d->manualKernOnly && next) {
1133 const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next); 1143 const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next);
1134 const int kern = stbtt_GetGlyphKernAdvance( 1144 int kern = stbtt_GetGlyphKernAdvance(
1135 &glyph->font->font, glyph->glyphIndex, nextGlyphIndex); 1145 &glyph->font->font, glyph->glyphIndex, nextGlyphIndex);
1146 /* Nunito needs some kerning fixes. */
1147 if (glyph->font->family == nunito_TextFont) {
1148 if (ch == 'W' && (next == 'i' || next == 'h')) {
1149 kern = -30;
1150 }
1151 else if (ch == 'T' && next == 'h') {
1152 kern = -15;
1153 }
1154 else if (ch == 'V' && next == 'i') {
1155 kern = -15;
1156 }
1157 }
1136 if (kern) { 1158 if (kern) {
1137// printf("%lc(%u) -> %lc(%u): kern %d (%f)\n", ch, glyph->glyphIndex, next, 1159// printf("%lc(%u) -> %lc(%u): kern %d (%f)\n", ch, glyph->glyphIndex, next,
1138// nextGlyphIndex, 1160// nextGlyphIndex,
1139// kern, d->xScale * kern); 1161// kern, d->xScale * kern);
1140 xpos += d->xScale * kern; 1162 xpos += glyph->font->xScale * kern;
1141 xposExtend += d->xScale * kern; 1163 xposExtend += glyph->font->xScale * kern;
1142 } 1164 }
1143 } 1165 }
1144 } 1166 }
diff --git a/src/ui/text.h b/src/ui/text.h
index dd377247..5a099142 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -117,7 +117,8 @@ iLocalDef iBool isJapanese_FontId(enum iFontId id) {
117#define emojiVariationSelector_Char ((iChar) 0xfe0f) 117#define emojiVariationSelector_Char ((iChar) 0xfe0f)
118 118
119enum iTextFont { 119enum iTextFont {
120 nunito_TextFont, 120 undefined_TextFont = -1,
121 nunito_TextFont = 0,
121 firaSans_TextFont, 122 firaSans_TextFont,
122 literata_TextFont, 123 literata_TextFont,
123 tinos_TextFont, 124 tinos_TextFont,