diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-08-20 06:36:07 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-08-20 06:36:07 +0300 |
commit | 4e62a21cea5781fc91ec5ef22710e0fe19badb3c (patch) | |
tree | dc688c51fcec9f990796b4e58f0422dc25935954 | |
parent | c4fa62d07dac1c3855dae4ed5108ab0f3eb43ceb (diff) | |
parent | d358811295527f705defbeea31b38fa2ee90a12e (diff) |
Merge branch 'dev' into work/v1.7
# Conflicts:
# CMakeLists.txt
-rw-r--r-- | CMakeLists.txt | 10 | ||||
m--------- | lib/the_Foundation | 0 | ||||
-rw-r--r-- | res/about/version.gmi | 10 | ||||
-rw-r--r-- | res/fi.skyjake.Lagrange.appdata.xml | 32 | ||||
-rw-r--r-- | src/app.c | 2 | ||||
-rw-r--r-- | src/gmutil.c | 41 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 11 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 87 | ||||
-rw-r--r-- | src/ui/text.c | 7 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 2 | ||||
-rw-r--r-- | src/ui/window.c | 34 |
11 files changed, 162 insertions, 74 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index fc0835f1..73e2b8d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -28,6 +28,12 @@ if (IOS) | |||
28 | set (IOS_BUNDLE_VERSION 21.6.15) | 28 | set (IOS_BUNDLE_VERSION 21.6.15) |
29 | endif () | 29 | endif () |
30 | 30 | ||
31 | # Default that depend on environment. | ||
32 | set (DEFAULT_RESIZE_DRAW ON) | ||
33 | if (HAIKU) | ||
34 | set (DEFAULT_RESIZE_DRAW OFF) | ||
35 | endif () | ||
36 | |||
31 | # Build configuration. | 37 | # Build configuration. |
32 | option (ENABLE_CUSTOM_FRAME "Draw a custom window frame (Windows)" OFF) | 38 | option (ENABLE_CUSTOM_FRAME "Draw a custom window frame (Windows)" OFF) |
33 | option (ENABLE_DOWNLOAD_EDIT "Allow changing the Downloads directory" ON) | 39 | option (ENABLE_DOWNLOAD_EDIT "Allow changing the Downloads directory" ON) |
@@ -40,6 +46,7 @@ option (ENABLE_IPC "Use IPC to communicate between running instance | |||
40 | option (ENABLE_KERNING "Enable kerning in font renderer (slower)" ON) | 46 | option (ENABLE_KERNING "Enable kerning in font renderer (slower)" ON) |
41 | option (ENABLE_MPG123 "Use mpg123 for decoding MPEG audio" ON) | 47 | option (ENABLE_MPG123 "Use mpg123 for decoding MPEG audio" ON) |
42 | option (ENABLE_RELATIVE_EMBED "Resources should always be found via relative path" OFF) | 48 | option (ENABLE_RELATIVE_EMBED "Resources should always be found via relative path" OFF) |
49 | option (ENABLE_RESIZE_DRAW "Force window to redraw during resizing" ${DEFAULT_RESIZE_DRAW}) | ||
43 | option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) | 50 | option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) |
44 | option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF) | 51 | option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF) |
45 | option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF) | 52 | option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF) |
@@ -317,6 +324,9 @@ endif () | |||
317 | if (ENABLE_CUSTOM_FRAME AND MSYS) | 324 | if (ENABLE_CUSTOM_FRAME AND MSYS) |
318 | target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_CUSTOM_FRAME=1) | 325 | target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_CUSTOM_FRAME=1) |
319 | endif () | 326 | endif () |
327 | if (ENABLE_RESIZE_DRAW) | ||
328 | target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_RESIZE_DRAW=1) | ||
329 | endif () | ||
320 | target_link_libraries (app PUBLIC the_Foundation::the_Foundation) | 330 | target_link_libraries (app PUBLIC the_Foundation::the_Foundation) |
321 | target_link_libraries (app PUBLIC ${SDL2_LDFLAGS}) | 331 | target_link_libraries (app PUBLIC ${SDL2_LDFLAGS}) |
322 | if (ENABLE_HARFBUZZ AND HARFBUZZ_FOUND) | 332 | if (ENABLE_HARFBUZZ AND HARFBUZZ_FOUND) |
diff --git a/lib/the_Foundation b/lib/the_Foundation | |||
Subproject 4a2cd8896bf7fabbe7345c245a052083c6c1c3e | Subproject 7ff1adf809c047a349243e6e142a08d52dd6971 | ||
diff --git a/res/about/version.gmi b/res/about/version.gmi index 1fce4188..89e81047 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -6,6 +6,16 @@ | |||
6 | ``` | 6 | ``` |
7 | # Release notes | 7 | # Release notes |
8 | 8 | ||
9 | ## 1.6.4 | ||
10 | * Local files containing UTF-8 text can be viewed regardless of their file extension. | ||
11 | * Fixed input field cursor positioning and insertion problems around Emoji variation selectors. | ||
12 | * Fixed "Unknown Status Code" shown in Page Information for valid status codes. | ||
13 | * Fixed an issue with network requests that would make it appear the server was not responding, but the request would not time out. | ||
14 | * Fixed a potential invalid memory access when clicking on sidebar items. | ||
15 | * OpenBSD: Fixed a freeze after a network request is cancelled. | ||
16 | * Fixed page contents not reflowing during a window resize. | ||
17 | * Added build option ENABLE_RESIZE_DRAW. SDL doesn't redraw window contents on all platforms during resizing, so this can be used to force it. | ||
18 | |||
9 | ## 1.6.3 | 19 | ## 1.6.3 |
10 | * Select all text in an input field using Shift+Ctrl+A (macOS: ⌘A). | 20 | * Select all text in an input field using Shift+Ctrl+A (macOS: ⌘A). |
11 | * Input fields do not lose focus when the window becomes inactive, making it easier to resume input afterwards. | 21 | * Input fields do not lose focus when the window becomes inactive, making it easier to resume input afterwards. |
diff --git a/res/fi.skyjake.Lagrange.appdata.xml b/res/fi.skyjake.Lagrange.appdata.xml index 0259aa1e..4202a9b6 100644 --- a/res/fi.skyjake.Lagrange.appdata.xml +++ b/res/fi.skyjake.Lagrange.appdata.xml | |||
@@ -45,6 +45,36 @@ | |||
45 | <update_contact>jaakko.keranen@iki.fi</update_contact> | 45 | <update_contact>jaakko.keranen@iki.fi</update_contact> |
46 | 46 | ||
47 | <releases> | 47 | <releases> |
48 | <release version="1.6.4" date="2021-08-22"> | ||
49 | <description> | ||
50 | <p>Version 1.6 adds support for bidirectional text and complex scripts, | ||
51 | right-to-left paragraph layout, uploads using the Titan protocol, | ||
52 | and has an improved mechanism for tracking trust in server | ||
53 | certificates. Page contents can be fully cached in memory for more | ||
54 | efficient backward navigation. There are also UI improvements like | ||
55 | a reorganized Preferences and a setting for smooth scrolling | ||
56 | speed.</p> | ||
57 | <p>Changes and fixes in v1.6.4:</p> | ||
58 | <ul> | ||
59 | <li>Local files containing UTF-8 text can be viewed regardless of | ||
60 | their file extension.</li> | ||
61 | <li>Fixed input field cursor positioning and insertion problems | ||
62 | around Emoji variation selectors.</li> | ||
63 | <li>Fixed "Unknown Status Code" shown in Page Information for | ||
64 | valid status codes.</li> | ||
65 | <li>Fixed an issue with network requests that would make it appear | ||
66 | the server was not responding, but the request would not time | ||
67 | out.</li> | ||
68 | <li>Fixed a potential invalid memory access when clicking on | ||
69 | sidebar items.</li> | ||
70 | <li>Fixed a potential freeze after a network request is | ||
71 | cancelled.</li> | ||
72 | </ul> | ||
73 | <p>The full release notes can be viewed inside the app by opening | ||
74 | the "about:version" page.</p> | ||
75 | </description> | ||
76 | <url>https://github.com/skyjake/lagrange/releases/tag/v1.6.4</url> | ||
77 | </release> | ||
48 | <release version="1.6.3" date="2021-08-15"> | 78 | <release version="1.6.3" date="2021-08-15"> |
49 | <description> | 79 | <description> |
50 | <p>Version 1.6 adds support for bidirectional text and complex scripts, | 80 | <p>Version 1.6 adds support for bidirectional text and complex scripts, |
@@ -78,7 +108,7 @@ | |||
78 | the "about:version" page.</p> | 108 | the "about:version" page.</p> |
79 | </description> | 109 | </description> |
80 | <url>https://github.com/skyjake/lagrange/releases/tag/v1.6.3</url> | 110 | <url>https://github.com/skyjake/lagrange/releases/tag/v1.6.3</url> |
81 | </release> | 111 | </release> |
82 | <release version="1.6.2" date="2021-08-03"> | 112 | <release version="1.6.2" date="2021-08-03"> |
83 | <description> | 113 | <description> |
84 | <p>Version 1.6 adds support for bidirectional text and complex scripts, | 114 | <p>Version 1.6 adds support for bidirectional text and complex scripts, |
@@ -1351,7 +1351,7 @@ static int run_App_(iApp *d) { | |||
1351 | } | 1351 | } |
1352 | d->isRunning = iTrue; | 1352 | d->isRunning = iTrue; |
1353 | SDL_EventState(SDL_DROPFILE, SDL_ENABLE); /* open files via drag'n'drop */ | 1353 | SDL_EventState(SDL_DROPFILE, SDL_ENABLE); /* open files via drag'n'drop */ |
1354 | #if defined (iPlatformDesktop) | 1354 | #if defined (LAGRANGE_ENABLE_RESIZE_DRAW) |
1355 | SDL_AddEventWatch(resizeWatcher_, d); /* redraw window during resizing */ | 1355 | SDL_AddEventWatch(resizeWatcher_, d); /* redraw window during resizing */ |
1356 | #endif | 1356 | #endif |
1357 | while (d->isRunning) { | 1357 | while (d->isRunning) { |
diff --git a/src/gmutil.c b/src/gmutil.c index fda8489b..9bd74ee0 100644 --- a/src/gmutil.c +++ b/src/gmutil.c | |||
@@ -22,10 +22,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "gmutil.h" | 23 | #include "gmutil.h" |
24 | 24 | ||
25 | #include <the_Foundation/regexp.h> | 25 | #include <the_Foundation/file.h> |
26 | #include <the_Foundation/fileinfo.h> | ||
26 | #include <the_Foundation/object.h> | 27 | #include <the_Foundation/object.h> |
27 | #include <the_Foundation/path.h> | 28 | #include <the_Foundation/path.h> |
28 | #include <the_Foundation/regexp.h> | 29 | #include <the_Foundation/regexp.h> |
30 | #include <the_Foundation/regexp.h> | ||
29 | 31 | ||
30 | iRegExp *newGemtextLink_RegExp(void) { | 32 | iRegExp *newGemtextLink_RegExp(void) { |
31 | return new_RegExp("=>\\s*([^\\s]+)(\\s.*)?", 0); | 33 | return new_RegExp("=>\\s*([^\\s]+)(\\s.*)?", 0); |
@@ -513,18 +515,6 @@ const char *mediaType_Path(const iString *path) { | |||
513 | if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { | 515 | if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { |
514 | return "text/gemini; charset=utf-8"; | 516 | return "text/gemini; charset=utf-8"; |
515 | } | 517 | } |
516 | /* TODO: It would be better to default to text/plain, but switch to | ||
517 | application/octet-stream if the contents fail to parse as UTF-8. */ | ||
518 | else if (endsWithCase_String(path, ".txt") || | ||
519 | endsWithCase_String(path, ".md") || | ||
520 | endsWithCase_String(path, ".c") || | ||
521 | endsWithCase_String(path, ".h") || | ||
522 | endsWithCase_String(path, ".cc") || | ||
523 | endsWithCase_String(path, ".hh") || | ||
524 | endsWithCase_String(path, ".cpp") || | ||
525 | endsWithCase_String(path, ".hpp")) { | ||
526 | return "text/plain"; | ||
527 | } | ||
528 | else if (endsWithCase_String(path, ".pem")) { | 518 | else if (endsWithCase_String(path, ".pem")) { |
529 | return "application/x-pem-file"; | 519 | return "application/x-pem-file"; |
530 | } | 520 | } |
@@ -558,7 +548,30 @@ const char *mediaType_Path(const iString *path) { | |||
558 | else if (endsWithCase_String(path, ".mid")) { | 548 | else if (endsWithCase_String(path, ".mid")) { |
559 | return "audio/midi"; | 549 | return "audio/midi"; |
560 | } | 550 | } |
561 | return "application/octet-stream"; | 551 | else if (endsWithCase_String(path, ".txt") || |
552 | endsWithCase_String(path, ".md") || | ||
553 | endsWithCase_String(path, ".c") || | ||
554 | endsWithCase_String(path, ".h") || | ||
555 | endsWithCase_String(path, ".cc") || | ||
556 | endsWithCase_String(path, ".hh") || | ||
557 | endsWithCase_String(path, ".cpp") || | ||
558 | endsWithCase_String(path, ".hpp")) { | ||
559 | return "text/plain"; | ||
560 | } | ||
561 | const char *mtype = "application/octet-stream"; | ||
562 | /* If the file is reasonably small and looks like UTF-8, we'll display it as text/plain. */ | ||
563 | if (fileExists_FileInfo(path) && fileSize_FileInfo(path) <= 5000000) { | ||
564 | iFile *f = new_File(path); | ||
565 | if (open_File(f, readOnly_FileMode)) { | ||
566 | iBlock *content = readAll_File(f); | ||
567 | if (isUtf8_Rangecc(range_Block(content))) { | ||
568 | mtype = "text/plain; charset=utf-8"; | ||
569 | } | ||
570 | delete_Block(content); | ||
571 | } | ||
572 | iRelease(f); | ||
573 | } | ||
574 | return mtype; | ||
562 | } | 575 | } |
563 | 576 | ||
564 | static void replaceAllChars_String_(iString *d, char c, const char *replacement) { | 577 | static void replaceAllChars_String_(iString *d, char c, const char *replacement) { |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index a89eb0eb..3f655db5 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1863,7 +1863,12 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1863 | updateTrust_DocumentWidget_(d, resp); | 1863 | updateTrust_DocumentWidget_(d, resp); |
1864 | init_Anim(&d->sideOpacity, 0); | 1864 | init_Anim(&d->sideOpacity, 0); |
1865 | init_Anim(&d->altTextOpacity, 0); | 1865 | init_Anim(&d->altTextOpacity, 0); |
1866 | format_String(&d->sourceHeader, "%d %s", statusCode, get_GmError(statusCode)->title); | 1866 | format_String(&d->sourceHeader, |
1867 | "%d %s", | ||
1868 | statusCode, | ||
1869 | isEmpty_String(&resp->meta) && !isSuccess_GmStatusCode(statusCode) | ||
1870 | ? get_GmError(statusCode)->title | ||
1871 | : cstr_String(&resp->meta)); | ||
1867 | d->sourceStatus = statusCode; | 1872 | d->sourceStatus = statusCode; |
1868 | switch (category_GmStatusCode(statusCode)) { | 1873 | switch (category_GmStatusCode(statusCode)) { |
1869 | case categoryInput_GmStatusCode: { | 1874 | case categoryInput_GmStatusCode: { |
@@ -2773,14 +2778,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2773 | id_GmRequest(d->request) == argU32Label_Command(cmd, "reqid")) { | 2778 | id_GmRequest(d->request) == argU32Label_Command(cmd, "reqid")) { |
2774 | set_Block(&d->sourceContent, body_GmRequest(d->request)); | 2779 | set_Block(&d->sourceContent, body_GmRequest(d->request)); |
2775 | if (!isSuccess_GmStatusCode(status_GmRequest(d->request))) { | 2780 | if (!isSuccess_GmStatusCode(status_GmRequest(d->request))) { |
2781 | /* TODO: Why is this here? Can it be removed? */ | ||
2776 | format_String(&d->sourceHeader, | 2782 | format_String(&d->sourceHeader, |
2777 | "%d %s", | 2783 | "%d %s", |
2778 | status_GmRequest(d->request), | 2784 | status_GmRequest(d->request), |
2779 | cstr_String(meta_GmRequest(d->request))); | 2785 | cstr_String(meta_GmRequest(d->request))); |
2780 | } | 2786 | } |
2781 | else { | ||
2782 | clear_String(&d->sourceHeader); | ||
2783 | } | ||
2784 | updateFetchProgress_DocumentWidget_(d); | 2787 | updateFetchProgress_DocumentWidget_(d); |
2785 | checkResponse_DocumentWidget_(d); | 2788 | checkResponse_DocumentWidget_(d); |
2786 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { | 2789 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 9f233345..2f0cabbb 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -360,35 +360,64 @@ static int endX_InputWidget_(const iInputWidget *d, int y) { | |||
360 | return line->range.end - (isLastLine_InputWidget_(d, line) ? 0 : 1) - line->range.start; | 360 | return line->range.end - (isLastLine_InputWidget_(d, line) ? 0 : 1) - line->range.start; |
361 | } | 361 | } |
362 | 362 | ||
363 | static iBool isCursorFocusable_Char_(iChar c) { | ||
364 | return !isDefaultIgnorable_Char(c) && | ||
365 | !isVariationSelector_Char(c) && | ||
366 | !isFitzpatrickType_Char(c); | ||
367 | } | ||
368 | |||
369 | static iChar at_InputWidget_(const iInputWidget *d, iInt2 pos) { | ||
370 | if (pos.y >= 0 && pos.y < size_Array(&d->lines) && | ||
371 | pos.x >= 0 && pos.x <= endX_InputWidget_(d, pos.y)) { | ||
372 | iChar ch = 0; | ||
373 | decodeBytes_MultibyteChar(charPos_InputWidget_(d, pos), | ||
374 | constEnd_String(lineString_InputWidget_(d, pos.y)), | ||
375 | &ch); | ||
376 | return ch; | ||
377 | } | ||
378 | return ' '; | ||
379 | } | ||
380 | |||
363 | static iInt2 movedCursor_InputWidget_(const iInputWidget *d, iInt2 pos, int xDir, int yDir) { | 381 | static iInt2 movedCursor_InputWidget_(const iInputWidget *d, iInt2 pos, int xDir, int yDir) { |
364 | iChar ch; | 382 | iChar ch = 0; |
365 | if (xDir < 0) { | 383 | int n = 0; |
366 | if (pos.x == 0) { | 384 | /* TODO: The cursor should never land on any combining codepoints either. */ |
367 | if (pos.y > 0) { | 385 | for (;;) { |
368 | pos.x = endX_InputWidget_(d, --pos.y); | 386 | if (xDir < 0) { |
387 | if (pos.x == 0) { | ||
388 | if (pos.y > 0) { | ||
389 | pos.x = endX_InputWidget_(d, --pos.y); | ||
390 | } | ||
369 | } | 391 | } |
370 | } | 392 | else { |
371 | else { | 393 | iAssert(pos.x > 0); |
372 | iAssert(pos.x > 0); | 394 | n = decodePrecedingBytes_MultibyteChar(charPos_InputWidget_(d, pos), |
373 | int n = decodePrecedingBytes_MultibyteChar(charPos_InputWidget_(d, pos), | ||
374 | cstr_String(lineString_InputWidget_(d, pos.y)), | 395 | cstr_String(lineString_InputWidget_(d, pos.y)), |
375 | &ch); | 396 | &ch); |
376 | pos.x -= n; | 397 | pos.x -= n; |
377 | } | 398 | if (!isCursorFocusable_Char_(at_InputWidget_(d, pos))) { |
378 | } | 399 | continue; |
379 | else if (xDir > 0) { | 400 | } |
380 | if (pos.x == endX_InputWidget_(d, pos.y)) { | ||
381 | if (pos.y < size_Array(&d->lines) - 1) { | ||
382 | pos.y++; | ||
383 | pos.x = 0; | ||
384 | } | 401 | } |
385 | } | 402 | } |
386 | else { | 403 | else if (xDir > 0) { |
387 | int n = decodeBytes_MultibyteChar(charPos_InputWidget_(d, pos), | 404 | if (pos.x == endX_InputWidget_(d, pos.y)) { |
405 | if (pos.y < size_Array(&d->lines) - 1) { | ||
406 | pos.y++; | ||
407 | pos.x = 0; | ||
408 | } | ||
409 | } | ||
410 | else { | ||
411 | n = decodeBytes_MultibyteChar(charPos_InputWidget_(d, pos), | ||
388 | constEnd_String(lineString_InputWidget_(d, pos.y)), | 412 | constEnd_String(lineString_InputWidget_(d, pos.y)), |
389 | &ch); | 413 | &ch); |
390 | pos.x += n; | 414 | pos.x += n; |
415 | if (!isCursorFocusable_Char_(at_InputWidget_(d, pos))) { | ||
416 | continue; | ||
417 | } | ||
418 | } | ||
391 | } | 419 | } |
420 | break; | ||
392 | } | 421 | } |
393 | return pos; | 422 | return pos; |
394 | } | 423 | } |
@@ -1026,7 +1055,9 @@ static void insertRange_InputWidget_(iInputWidget *d, iRangecc range) { | |||
1026 | cstr_String(&line->text) + d->cursor.x, constEnd_String(&line->text) | 1055 | cstr_String(&line->text) + d->cursor.x, constEnd_String(&line->text) |
1027 | }); | 1056 | }); |
1028 | truncate_String(&line->text, d->cursor.x); | 1057 | truncate_String(&line->text, d->cursor.x); |
1029 | appendCStr_String(&line->text, "\n"); | 1058 | if (!endsWith_String(&line->text, "\n")) { |
1059 | appendCStr_String(&line->text, "\n"); | ||
1060 | } | ||
1030 | insert_Array(&d->lines, ++d->cursor.y, &split); | 1061 | insert_Array(&d->lines, ++d->cursor.y, &split); |
1031 | d->cursor.x = 0; | 1062 | d->cursor.x = 0; |
1032 | } | 1063 | } |
@@ -1059,6 +1090,8 @@ void setCursor_InputWidget(iInputWidget *d, iInt2 pos) { | |||
1059 | iAssert(!isEmpty_Array(&d->lines)); | 1090 | iAssert(!isEmpty_Array(&d->lines)); |
1060 | pos.x = iClamp(pos.x, 0, endX_InputWidget_(d, pos.y)); | 1091 | pos.x = iClamp(pos.x, 0, endX_InputWidget_(d, pos.y)); |
1061 | d->cursor = pos; | 1092 | d->cursor = pos; |
1093 | iChar ch = at_InputWidget_(d, pos); | ||
1094 | printf("cursor x:%d ch:%08x (%lc)\n", pos.x, ch, (int)ch); | ||
1062 | /* Update selection. */ | 1095 | /* Update selection. */ |
1063 | if (isMarking_()) { | 1096 | if (isMarking_()) { |
1064 | if (isEmpty_Range(&d->mark)) { | 1097 | if (isEmpty_Range(&d->mark)) { |
@@ -1221,18 +1254,6 @@ static iBool deleteMarked_InputWidget_(iInputWidget *d) { | |||
1221 | return iFalse; | 1254 | return iFalse; |
1222 | } | 1255 | } |
1223 | 1256 | ||
1224 | static iChar at_InputWidget_(const iInputWidget *d, iInt2 pos) { | ||
1225 | if (pos.y >= 0 && pos.y < size_Array(&d->lines) && | ||
1226 | pos.x >= 0 && pos.x <= endX_InputWidget_(d, pos.y)) { | ||
1227 | iChar ch = 0; | ||
1228 | decodeBytes_MultibyteChar(charPos_InputWidget_(d, pos), | ||
1229 | constEnd_String(lineString_InputWidget_(d, pos.y)), | ||
1230 | &ch); | ||
1231 | return ch; | ||
1232 | } | ||
1233 | return ' '; | ||
1234 | } | ||
1235 | |||
1236 | static iBool isWordChar_InputWidget_(const iInputWidget *d, iInt2 pos) { | 1257 | static iBool isWordChar_InputWidget_(const iInputWidget *d, iInt2 pos) { |
1237 | return isAlphaNumeric_Char(at_InputWidget_(d, pos)); | 1258 | return isAlphaNumeric_Char(at_InputWidget_(d, pos)); |
1238 | } | 1259 | } |
diff --git a/src/ui/text.c b/src/ui/text.c index 0a8386e4..639d8f13 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -1433,6 +1433,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1433 | iBool isFirst = iTrue; | 1433 | iBool isFirst = iTrue; |
1434 | const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2()); | 1434 | const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2()); |
1435 | const iBool checkHitChar = wrap && wrap->hitChar; | 1435 | const iBool checkHitChar = wrap && wrap->hitChar; |
1436 | iBool wasCharHit = iFalse; | ||
1436 | while (!isEmpty_Range(&wrapRuns)) { | 1437 | while (!isEmpty_Range(&wrapRuns)) { |
1437 | if (isFirst) { | 1438 | if (isFirst) { |
1438 | isFirst = iFalse; | 1439 | isFirst = iFalse; |
@@ -1452,9 +1453,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1452 | for (size_t runIndex = wrapRuns.start; runIndex < wrapRuns.end; runIndex++) { | 1453 | for (size_t runIndex = wrapRuns.start; runIndex < wrapRuns.end; runIndex++) { |
1453 | const iAttributedRun *run = at_Array(&attrText.runs, runIndex); | 1454 | const iAttributedRun *run = at_Array(&attrText.runs, runIndex); |
1454 | if (run->flags.isLineBreak) { | 1455 | if (run->flags.isLineBreak) { |
1455 | if (checkHitChar) { | 1456 | if (checkHitChar && !wasCharHit) { |
1456 | if (wrap->hitChar == sourcePtr_AttributedText_(&attrText, run->logical.start)) { | 1457 | if (wrap->hitChar == sourcePtr_AttributedText_(&attrText, run->logical.start)) { |
1457 | wrap->hitAdvance_out = init_I2(wrapAdvance, yCursor); | 1458 | wrap->hitAdvance_out = init_I2(wrapAdvance, yCursor); |
1459 | wasCharHit = iTrue; | ||
1458 | } | 1460 | } |
1459 | } | 1461 | } |
1460 | wrapPosRange.end = run->logical.start; | 1462 | wrapPosRange.end = run->logical.start; |
@@ -1479,9 +1481,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1479 | if (logPos < wrapPosRange.start || logPos >= wrapPosRange.end) { | 1481 | if (logPos < wrapPosRange.start || logPos >= wrapPosRange.end) { |
1480 | continue; | 1482 | continue; |
1481 | } | 1483 | } |
1482 | if (checkHitChar) { | 1484 | if (checkHitChar && !wasCharHit) { |
1483 | if (wrap->hitChar == sourcePtr_AttributedText_(&attrText, logPos)) { | 1485 | if (wrap->hitChar == sourcePtr_AttributedText_(&attrText, logPos)) { |
1484 | wrap->hitAdvance_out = init_I2(wrapAdvance, yCursor); | 1486 | wrap->hitAdvance_out = init_I2(wrapAdvance, yCursor); |
1487 | wasCharHit = iTrue; | ||
1485 | } | 1488 | } |
1486 | } | 1489 | } |
1487 | /* Check if the hit point is on the left side of this line. */ | 1490 | /* Check if the hit point is on the left side of this line. */ |
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index 57b6b6b7..5e1ee493 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c | |||
@@ -149,7 +149,7 @@ void init_UploadWidget(iUploadWidget *d) { | |||
149 | iWidget *buttons = | 149 | iWidget *buttons = |
150 | makeDialogButtons_Widget((iMenuItem[]){ { "${upload.port}", 0, 0, "upload.setport" }, | 150 | makeDialogButtons_Widget((iMenuItem[]){ { "${upload.port}", 0, 0, "upload.setport" }, |
151 | { "---", 0, 0, NULL }, | 151 | { "---", 0, 0, NULL }, |
152 | { "${cancel}", SDLK_ESCAPE, 0, "upload.cancel" }, | 152 | { "${close}", SDLK_ESCAPE, 0, "upload.cancel" }, |
153 | { uiTextAction_ColorEscape "${dlg.upload.send}", | 153 | { uiTextAction_ColorEscape "${dlg.upload.send}", |
154 | SDLK_RETURN, | 154 | SDLK_RETURN, |
155 | KMOD_PRIMARY, | 155 | KMOD_PRIMARY, |
diff --git a/src/ui/window.c b/src/ui/window.c index 86d22b1c..1727b988 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -219,25 +219,23 @@ static void updateSize_Window_(iWindow *d, iBool notifyAlways) { | |||
219 | size->y -= d->keyboardHeight; | 219 | size->y -= d->keyboardHeight; |
220 | if (notifyAlways || !isEqual_I2(oldSize, *size)) { | 220 | if (notifyAlways || !isEqual_I2(oldSize, *size)) { |
221 | windowSizeChanged_Window_(d); | 221 | windowSizeChanged_Window_(d); |
222 | const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); | 222 | if (!isEqual_I2(*size, d->place.lastNotifiedSize)) { |
223 | const iBool isVert = (d->place.lastNotifiedSize.y != size->y); | 223 | const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); |
224 | postCommandf_App("window.resized width:%d height:%d horiz:%d vert:%d", | 224 | const iBool isVert = (d->place.lastNotifiedSize.y != size->y); |
225 | size->x, | 225 | postCommandf_App("window.resized width:%d height:%d horiz:%d vert:%d", |
226 | size->y, | 226 | size->x, |
227 | isHoriz, | 227 | size->y, |
228 | isVert); | 228 | isHoriz, |
229 | postCommand_App("widget.overflow"); /* check bounds with updated sizes */ | 229 | isVert); |
230 | postCommand_App("widget.overflow"); /* check bounds with updated sizes */ | ||
231 | } | ||
230 | postRefresh_App(); | 232 | postRefresh_App(); |
231 | d->place.lastNotifiedSize = *size; | 233 | d->place.lastNotifiedSize = *size; |
232 | } | 234 | } |
233 | } | 235 | } |
234 | 236 | ||
235 | void drawWhileResizing_Window(iWindow *d, int w, int h) { | 237 | void drawWhileResizing_Window(iWindow *d, int w, int h) { |
236 | /* This is called while a window resize is in progress, so we can be pretty confident | 238 | draw_Window(d); |
237 | the size has actually changed. */ | ||
238 | d->size = coord_Window(d, w, h); | ||
239 | windowSizeChanged_Window_(d); | ||
240 | draw_Window(d); | ||
241 | } | 239 | } |
242 | 240 | ||
243 | static float pixelRatio_Window_(const iWindow *d) { | 241 | static float pixelRatio_Window_(const iWindow *d) { |
@@ -489,7 +487,7 @@ void init_Window(iWindow *d, iRect rect) { | |||
489 | SDL_GetRendererOutputSize(d->render, &d->size.x, &d->size.y); | 487 | SDL_GetRendererOutputSize(d->render, &d->size.x, &d->size.y); |
490 | setupUserInterface_Window(d); | 488 | setupUserInterface_Window(d); |
491 | postCommand_App("~bindings.changed"); /* update from bindings */ | 489 | postCommand_App("~bindings.changed"); /* update from bindings */ |
492 | updateSize_Window_(d, iFalse); | 490 | //updateSize_Window_(d, iFalse); |
493 | /* Load the border shadow texture. */ { | 491 | /* Load the border shadow texture. */ { |
494 | SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0); | 492 | SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0); |
495 | d->borderShadow = SDL_CreateTextureFromSurface(d->render, surf); | 493 | d->borderShadow = SDL_CreateTextureFromSurface(d->render, surf); |
@@ -728,7 +726,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { | |||
728 | } | 726 | } |
729 | case SDL_WINDOWEVENT_RESIZED: | 727 | case SDL_WINDOWEVENT_RESIZED: |
730 | if (d->isMinimized) { | 728 | if (d->isMinimized) { |
731 | updateSize_Window_(d, iTrue); | 729 | //updateSize_Window_(d, iTrue); |
732 | return iTrue; | 730 | return iTrue; |
733 | } | 731 | } |
734 | if (unsnap_Window_(d, NULL)) { | 732 | if (unsnap_Window_(d, NULL)) { |
@@ -739,7 +737,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { | |||
739 | //printf("normal rect set (resize)\n"); fflush(stdout); | 737 | //printf("normal rect set (resize)\n"); fflush(stdout); |
740 | } | 738 | } |
741 | checkPixelRatioChange_Window_(d); | 739 | checkPixelRatioChange_Window_(d); |
742 | updateSize_Window_(d, iTrue /* we were already redrawing during the resize */); | 740 | //updateSize_Window_(d, iTrue /* we were already redrawing during the resize */); |
743 | postRefresh_App(); | 741 | postRefresh_App(); |
744 | return iTrue; | 742 | return iTrue; |
745 | case SDL_WINDOWEVENT_RESTORED: | 743 | case SDL_WINDOWEVENT_RESTORED: |
@@ -1014,7 +1012,7 @@ void draw_Window(iWindow *d) { | |||
1014 | if (d->isDrawFrozen) { | 1012 | if (d->isDrawFrozen) { |
1015 | return; | 1013 | return; |
1016 | } | 1014 | } |
1017 | #if defined (iPlatformMobile) | 1015 | //#if defined (iPlatformMobile) |
1018 | /* Check if root needs resizing. */ { | 1016 | /* Check if root needs resizing. */ { |
1019 | iInt2 renderSize; | 1017 | iInt2 renderSize; |
1020 | SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y); | 1018 | SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y); |
@@ -1023,7 +1021,7 @@ void draw_Window(iWindow *d) { | |||
1023 | processEvents_App(postedEventsOnly_AppEventMode); | 1021 | processEvents_App(postedEventsOnly_AppEventMode); |
1024 | } | 1022 | } |
1025 | } | 1023 | } |
1026 | #endif | 1024 | //#endif |
1027 | const int winFlags = SDL_GetWindowFlags(d->win); | 1025 | const int winFlags = SDL_GetWindowFlags(d->win); |
1028 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; | 1026 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; |
1029 | iPaint p; | 1027 | iPaint p; |