Release v0.6.14: resize correction settings and personal dictionaries
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.24)
|
cmake_minimum_required(VERSION 3.24)
|
||||||
project(KSwitcher VERSION 0.6.13 LANGUAGES CXX RC)
|
project(KSwitcher VERSION 0.6.14 LANGUAGES CXX RC)
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
Binary file not shown.
+8
-5
@@ -1,14 +1,17 @@
|
|||||||
# Latest Windows release
|
# Latest Windows release
|
||||||
|
|
||||||
## v0.6.13 — 2026-10-30
|
## v0.6.14 — 2026-10-30
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
- Correct words containing punctuation keys that become letters in the target layout, such as `hj,bnm` → `робить`. Validate letters and capitalization after conversion so personal dictionary entries can match.
|
- Restore resizing for the automatic-correction settings dialog with an adaptive layout.
|
||||||
- Extend automatic word selection across internal punctuation only while the selected text matches a suffix of the tracked input. Verify the complete tracked word before replacing it.
|
- Open the dialog with larger personal-dictionary fields. The three dictionaries share additional width evenly and receive additional height as the window grows.
|
||||||
- Give the fixed-layout automatic-correction settings dialog a fixed window size, retaining scrolling on small displays instead of allowing empty space when resized.
|
- Expand exclusion lists and punctuation fields horizontally, and move lower settings and action buttons with the window while preserving spacing.
|
||||||
|
- Retain scrolling when the dialog must fit a small screen.
|
||||||
|
|
||||||
|
Includes the earlier automatic-correction and log-viewer improvements. Existing modern releases update directly to this version without installing intermediate releases.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
220 native regression checks, 466 input/UI checks, screenshot editor and notification registration checks, 45 updater checks and updater progress checks passed. Native editor integration verified continuous typing during correction and complete selection/conversion of `hj,bnm` to `робить`. The correction was also confirmed in the user's editor.
|
220 native regression checks, 472 input/UI checks including dictionary sizing and footer placement, screenshot editor and notification registration checks, 45 updater checks and updater progress checks. The resized dialog was also confirmed in the development build by the user.
|
||||||
|
|
||||||
**Upgrade:** choose Update in the tray menu. Legacy v0.3.0–v0.4.1 clients first receive the v0.4.2 compatibility bridge, then the current full release. v0.2.3 and earlier require manual installation.
|
**Upgrade:** choose Update in the tray menu. Legacy v0.3.0–v0.4.1 clients first receive the v0.4.2 compatibility bridge, then the current full release. v0.2.3 and earlier require manual installation.
|
||||||
For manual installation, exit KSwitcher and extract the entire ZIP into its installation folder. Keep personal `data/` and customized dictionaries.
|
For manual installation, exit KSwitcher and extract the entire ZIP into its installation folder. Keep personal `data/` and customized dictionaries.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
eae70031520882cc6a798e11a640fa101df01db12aeba4e6c96d8aa90037ae1e KSwitcher-win-x64.zip
|
4939b83b2cd641e969107091012fe12340b09d73ec1b7f7292c6809d226e05f5 KSwitcher-win-x64.zip
|
||||||
|
|||||||
+3
-3
@@ -9,8 +9,8 @@ IDC_SCREENSHOT_PENCIL CURSOR "icons/lucide/pencil.cur"
|
|||||||
1 RT_MANIFEST "app.manifest"
|
1 RT_MANIFEST "app.manifest"
|
||||||
201 RCDATA "update.ps1"
|
201 RCDATA "update.ps1"
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 0,6,13,0
|
FILEVERSION 0,6,14,0
|
||||||
PRODUCTVERSION 0,6,13,0
|
PRODUCTVERSION 0,6,14,0
|
||||||
FILEOS VOS_NT_WINDOWS32
|
FILEOS VOS_NT_WINDOWS32
|
||||||
FILETYPE VFT_APP
|
FILETYPE VFT_APP
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -20,7 +20,7 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
||||||
VALUE "ProductName", "KSwitcher"
|
VALUE "ProductName", "KSwitcher"
|
||||||
VALUE "FileVersion", "0.6.13"
|
VALUE "FileVersion", "0.6.14"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|||||||
+31
-2
@@ -35,6 +35,7 @@ struct WindowLayout
|
|||||||
int wheelRemainder{}, horizontalWheelRemainder{};
|
int wheelRemainder{}, horizontalWheelRemainder{};
|
||||||
std::vector<std::pair<HWND, RECT>> controls; // Original 96-DPI geometry; never rescale rounded pixels.
|
std::vector<std::pair<HWND, RECT>> controls; // Original 96-DPI geometry; never rescale rounded pixels.
|
||||||
HWND stretch{}, bottomLeft{}, bottomRight{};
|
HWND stretch{}, bottomLeft{}, bottomRight{};
|
||||||
|
bool autoOptions{};
|
||||||
};
|
};
|
||||||
WindowLayout *windowLayoutState(HWND w)
|
WindowLayout *windowLayoutState(HWND w)
|
||||||
{
|
{
|
||||||
@@ -70,7 +71,7 @@ void arrangeWindow(HWND w)
|
|||||||
layout->y = SetScrollInfo(w, SB_VERT, &vertical, TRUE);
|
layout->y = SetScrollInfo(w, SB_VERT, &vertical, TRUE);
|
||||||
}
|
}
|
||||||
RECT client{}; GetClientRect(w, &client);
|
RECT client{}; GetClientRect(w, &client);
|
||||||
bool expandable = layout->history || layout->stretch;
|
bool expandable = layout->history || layout->stretch || layout->autoOptions;
|
||||||
int extraWidth = expandable ? std::max(0, int(client.right) - scale(w, layout->width)) : 0;
|
int extraWidth = expandable ? std::max(0, int(client.right) - scale(w, layout->width)) : 0;
|
||||||
int extraHeight = expandable ? std::max(0, int(client.bottom) - scale(w, layout->height)) : 0;
|
int extraHeight = expandable ? std::max(0, int(client.bottom) - scale(w, layout->height)) : 0;
|
||||||
struct Move { HWND child; RECT rect; };
|
struct Move { HWND child; RECT rect; };
|
||||||
@@ -89,6 +90,28 @@ void arrangeWindow(HWND w)
|
|||||||
}
|
}
|
||||||
if (child == layout->bottomLeft || child == layout->bottomRight)
|
if (child == layout->bottomLeft || child == layout->bottomRight)
|
||||||
OffsetRect(&next, child == layout->bottomRight ? extraWidth : 0, extraHeight);
|
OffsetRect(&next, child == layout->bottomRight ? extraWidth : 0, extraHeight);
|
||||||
|
if (layout->autoOptions)
|
||||||
|
{
|
||||||
|
if (r.top >= 396) OffsetRect(&next, 0, extraHeight);
|
||||||
|
if (r.top == 302 || r.top == 328 || (id >= 401 && id <= 403))
|
||||||
|
{
|
||||||
|
int column = id >= 401 && id <= 403 ? id - 401 : (r.left - 18) / 178;
|
||||||
|
int leftExtra = extraWidth * column / 3;
|
||||||
|
OffsetRect(&next, leftExtra, 0);
|
||||||
|
next.right += extraWidth * (column + 1) / 3 - leftExtra;
|
||||||
|
if (r.top == 328) next.bottom += extraHeight;
|
||||||
|
}
|
||||||
|
else if (r.top == 474 || r.top == 500)
|
||||||
|
{
|
||||||
|
bool rightColumn = r.left >= 290;
|
||||||
|
OffsetRect(&next, rightColumn ? extraWidth / 2 : 0, 0);
|
||||||
|
next.right += rightColumn ? extraWidth - extraWidth / 2 : extraWidth / 2;
|
||||||
|
}
|
||||||
|
else if (id == 406 || id == 410 || id == IDOK || id == IDCANCEL)
|
||||||
|
OffsetRect(&next, extraWidth, 0);
|
||||||
|
else if (r.right - r.left >= 500)
|
||||||
|
next.right += extraWidth;
|
||||||
|
}
|
||||||
RECT current{}; GetWindowRect(child, ¤t);
|
RECT current{}; GetWindowRect(child, ¤t);
|
||||||
MapWindowPoints(nullptr, w, reinterpret_cast<POINT *>(¤t), 2);
|
MapWindowPoints(nullptr, w, reinterpret_cast<POINT *>(¤t), 2);
|
||||||
if (!EqualRect(¤t, &next)) moves.push_back({child, next});
|
if (!EqualRect(¤t, &next)) moves.push_back({child, next});
|
||||||
@@ -1072,7 +1095,8 @@ LRESULT CALLBACK autoOptionsProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
void showAutoOptions(HWND parent, SettingsView *v)
|
void showAutoOptions(HWND parent, SettingsView *v)
|
||||||
{
|
{
|
||||||
HWND w = makeWindow(*v->app, L"KSwitcher.AutoOptions", L"Automatic layout correction", autoOptionsProc,
|
HWND w = makeWindow(*v->app, L"KSwitcher.AutoOptions", L"Automatic layout correction", autoOptionsProc,
|
||||||
560, 680, 0, false);
|
560, 680);
|
||||||
|
windowLayoutState(w)->autoOptions = true;
|
||||||
SetWindowLongPtrW(w, GWLP_HWNDPARENT, (LONG_PTR)parent);
|
SetWindowLongPtrW(w, GWLP_HWNDPARENT, (LONG_PTR)parent);
|
||||||
SetWindowLongPtrW(w, GWLP_USERDATA, (LONG_PTR) new AutoOptionsView{parent, v});
|
SetWindowLongPtrW(w, GWLP_USERDATA, (LONG_PTR) new AutoOptionsView{parent, v});
|
||||||
label(w, L"Offline dictionaries (only layouts in your cycle are used)", 18, 16, 525);
|
label(w, L"Offline dictionaries (only layouts in your cycle are used)", 18, 16, 525);
|
||||||
@@ -1113,6 +1137,11 @@ void showAutoOptions(HWND parent, SettingsView *v)
|
|||||||
label(w, L"After editing files, click OK here, then Save in Settings to reload.", 18, 600, 525);
|
label(w, L"After editing files, click OK here, then Save in Settings to reload.", 18, 600, 525);
|
||||||
button(w, L"OK", IDOK, 326, 632, 100);
|
button(w, L"OK", IDOK, 326, 632, 100);
|
||||||
button(w, L"Cancel", IDCANCEL, 440, 632, 100);
|
button(w, L"Cancel", IDCANCEL, 440, 632, 100);
|
||||||
|
RECT initial{};
|
||||||
|
GetWindowRect(w, &initial);
|
||||||
|
initial.right += scale(w, 200);
|
||||||
|
initial.bottom += scale(w, 140);
|
||||||
|
fitToMonitor(w, initial);
|
||||||
EnableWindow(parent, FALSE);
|
EnableWindow(parent, FALSE);
|
||||||
showRequestedWindow(w);
|
showRequestedWindow(w);
|
||||||
SetForegroundWindow(w);
|
SetForegroundWindow(w);
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
namespace ks
|
namespace ks
|
||||||
{
|
{
|
||||||
inline constexpr wchar_t AppVersion[] = L"0.6.13";
|
inline constexpr wchar_t AppVersion[] = L"0.6.14";
|
||||||
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
||||||
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
||||||
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
||||||
|
|||||||
@@ -598,6 +598,35 @@ int main(int argc, char **argv) {
|
|||||||
auto autoOptions=FindWindowW(L"KSwitcher.AutoOptions",nullptr);
|
auto autoOptions=FindWindowW(L"KSwitcher.AutoOptions",nullptr);
|
||||||
require(autoOptions!=nullptr,"auto-correction options open");
|
require(autoOptions!=nullptr,"auto-correction options open");
|
||||||
ShowWindow(autoOptions,SW_HIDE);
|
ShowWindow(autoOptions,SW_HIDE);
|
||||||
|
{
|
||||||
|
require((GetWindowLongPtrW(autoOptions,GWL_STYLE)&WS_THICKFRAME)!=0,
|
||||||
|
"auto-correction dialog supports resizing");
|
||||||
|
RECT original{}, before[3]{}, after[3]{}, buttonBefore{}, buttonAfter{};
|
||||||
|
GetWindowRect(autoOptions,&original);
|
||||||
|
// Use a client size above the scrolling baseline at every DPI.
|
||||||
|
UINT dpi=GetDpiForWindow(autoOptions);
|
||||||
|
SetWindowPos(autoOptions,nullptr,0,0,MulDiv(800,dpi,96),MulDiv(900,dpi,96),
|
||||||
|
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
|
||||||
|
RECT baseline{}; GetWindowRect(autoOptions,&baseline);
|
||||||
|
for(int i=0;i<3;++i) GetWindowRect(GetDlgItem(autoOptions,411+i),&before[i]);
|
||||||
|
GetWindowRect(GetDlgItem(autoOptions,IDOK),&buttonBefore);
|
||||||
|
SetWindowPos(autoOptions,nullptr,0,0,baseline.right-baseline.left+240,
|
||||||
|
baseline.bottom-baseline.top+120,SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
|
||||||
|
for(int i=0;i<3;++i) {
|
||||||
|
GetWindowRect(GetDlgItem(autoOptions,411+i),&after[i]);
|
||||||
|
require(after[i].right-after[i].left==before[i].right-before[i].left+80 &&
|
||||||
|
after[i].bottom-after[i].top==before[i].bottom-before[i].top+120,
|
||||||
|
"dictionary columns share extra width and receive extra height");
|
||||||
|
}
|
||||||
|
GetWindowRect(GetDlgItem(autoOptions,IDOK),&buttonAfter);
|
||||||
|
require(buttonAfter.left-buttonBefore.left==240 && buttonAfter.top-buttonBefore.top==120,
|
||||||
|
"auto-correction buttons follow bottom-right corner");
|
||||||
|
require(after[0].right<after[1].left && after[1].right<after[2].left &&
|
||||||
|
after[2].bottom<buttonAfter.top,
|
||||||
|
"resized dictionary fields and footer do not overlap");
|
||||||
|
SetWindowPos(autoOptions,nullptr,0,0,original.right-original.left,original.bottom-original.top,
|
||||||
|
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
for(int id:{411,412,413}) {
|
for(int id:{411,412,413}) {
|
||||||
RECT control{},client{};
|
RECT control{},client{};
|
||||||
GetWindowRect(GetDlgItem(autoOptions,id),&control);
|
GetWindowRect(GetDlgItem(autoOptions,id),&control);
|
||||||
|
|||||||
Reference in New Issue
Block a user