[SETUP:REACTOS] Improve navigation in the setup wizard

- Support double-clicking on the Installation type page radio buttons.

  The Installation type page offers two self-excluding options:
  "Install ReactOS", and "Upgrade an existing ReactOS installation",
  and no other controls are available (except for the wizard navigation
  buttons). Thus, we can implement the UI feature of supporting double-
  clicked radio buttons as an abbreviation for "select + Next".

  For this, add the `BS_NOTIFY` style to both radio buttons, so as to
  receive the `BN_DBLCLK` notification via the `WM_COMMAND` message.

  Based on a suggestion by Raymond Chen,
  https://devblogs.microsoft.com/oldnewthing/20050804-10/?p=34713

- Ensure the correct radio button is selected when activating the
  Installation type page:
  ensure the "Install ReactOS" radio button is checked if we don't have
  a selected installation (default case), which can also happen if the
  user clicked on the "Do not upgrade" button on the Upgrade/Repair
  selection page, then went back.

- In the Upgrade/Repair selection page, go to the next page if the user
  double-clicked on a listed installation.

- In the Drives/Partitions page:
  * support pressing DELETE key to delete an existing partition;
  * support pressing ENTER key to install ReactOS on a eligible
    partition;
  * enable the "Create" partition button only if the selected disk region
    is not partitioned, and can be partitioned according to the disk's
    partitioning scheme (e.g. for MBR disks, no more than 4 primary
    partitions).
This commit is contained in:
Hermès Bélusca-Maïto
2026-07-29 16:49:15 +02:00
parent cb61fd4d71
commit ba58e3f528
2 changed files with 93 additions and 13 deletions
+45 -10
View File
@@ -1372,12 +1372,12 @@ DoCreatePartition(
_In_opt_ ULONGLONG SizeBytes,
_In_opt_ ULONG_PTR PartitionInfo)
{
BOOLEAN Success;
PPARTITEM PartItem;
PPARTENTRY PartEntry;
HTLITEM hParentItem;
HTLITEM hInsertAfter;
PPARTENTRY NextPart;
BOOLEAN Success;
PartItem = (pPartItem ? *pPartItem : GetItemPartition(hList, *phItem));
if (!PartItem)
@@ -1870,8 +1870,8 @@ DriveDlgProc(
PartItem = GetSelectedPartition(hList, &hItem);
if (!PartItem)
{
// If the button was clicked, a partition
// should have been selected first...
/* If the button was clicked, a partition
* should have been selected first */
ASSERT(FALSE);
break;
}
@@ -1914,6 +1914,33 @@ DriveDlgProc(
{
LPNMHDR lpnm = (LPNMHDR)lParam;
if (lpnm->idFrom == IDC_PARTITION) switch (lpnm->code)
{
case NM_RETURN:
{
/* If the currently selected item is a partition (and so, if
* the wizard "Next" button is enabled), pressing "Next" will
* attempt to select an install partition and go to the next
* page if successful. */
HWND hWndParent = GetParent(hwndDlg);
if (IsWindowEnabled(GetDlgItem(hWndParent, ID_WIZNEXT)))
PropSheet_PressButton(hWndParent, PSBTN_NEXT);
break;
}
case TVN_KEYDOWN:
{
if (((LPTV_KEYDOWN_EX)lParam)->wVKey == VK_DELETE)
{
/* Send the delete command only if IDC_PARTDELETE is
* enabled (i.e. the selected item IS a partition) */
if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_PARTDELETE)))
PostMessageW(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_PARTDELETE, BN_CLICKED), 0);
}
break;
}
}
// On Vista+ we can use TVN_ITEMCHANGED instead, with NMTVITEMCHANGE* pointer
if (lpnm->idFrom == IDC_PARTITION && lpnm->code == TVN_SELCHANGED)
{
@@ -1969,6 +1996,7 @@ DriveDlgProc(
/* Partition or unpartitioned space */
PPARTITEM PartItem = (PPARTITEM)pnmv->itemNew.lParam;
PPARTENTRY PartEntry;
BOOL CanBePartitioned;
ASSERT(PartItem);
PartEntry = PartItem->PartEntry;
ASSERT(PartEntry);
@@ -1977,11 +2005,17 @@ DriveDlgProc(
ShowDlgItem(hwndDlg, IDC_INITDISK, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_INITDISK, FALSE);
/* Check whether the selected disk region is not partitioned, and
* can be partitioned according to the disk's partitioning scheme */
CanBePartitioned = (!PartEntry->IsPartitioned &&
(PartitionCreateChecks(PartEntry, 0ULL, 0) == NOT_AN_ERROR));
if (!PartEntry->IsPartitioned)
{
/* Show and enable the "Create" partition button */
/* Show the "Create" partition button, but enable it
* only if the selected disk region can be partitioned */
ShowDlgItem(hwndDlg, IDC_PARTCREATE, SW_SHOW);
EnableDlgItem(hwndDlg, IDC_PARTCREATE, TRUE);
EnableDlgItem(hwndDlg, IDC_PARTCREATE, CanBePartitioned);
/* Hide and disable the "Format" button */
ShowDlgItem(hwndDlg, IDC_PARTFORMAT, SW_HIDE);
@@ -1993,12 +2027,14 @@ DriveDlgProc(
ShowDlgItem(hwndDlg, IDC_PARTCREATE, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTCREATE, FALSE);
/* Show the "Format" button, but enable or disable it if a formattable volume is present */
/* Show the "Format" button, but enable it only
* if a formattable volume is present */
ShowDlgItem(hwndDlg, IDC_PARTFORMAT, SW_SHOW);
EnableDlgItem(hwndDlg, IDC_PARTFORMAT, !!PartEntry->Volume);
}
/* Show the "Delete" partition button, but enable or disable it if the disk region is partitioned */
/* Show the "Delete" partition button, but enable it
* only if the disk region is partitioned */
ShowDlgItem(hwndDlg, IDC_PARTDELETE, SW_SHOW);
EnableDlgItem(hwndDlg, IDC_PARTDELETE, PartEntry->IsPartitioned);
@@ -2011,7 +2047,7 @@ DriveDlgProc(
* or it's not yet formatted (the installer will prompt
* for formatting parameters).
*
* 2. Or, the selected disk region is not partitioned but
* 2. Or, the selected disk region is not partitioned, but
* can be partitioned according to the disk's partitioning
* scheme (the installer will auto-partition the region
* and prompt for formatting parameters).
@@ -2021,8 +2057,7 @@ DriveDlgProc(
// TODO: In the future: first test needs to be augmented with:
// (... && PartEntry->Volume->IsSimpleVolume)
if ((PartEntry->IsPartitioned && PartEntry->Volume) ||
(!PartEntry->IsPartitioned && (PartitionCreateChecks(PartEntry, 0ULL, 0) == NOT_AN_ERROR)))
if ((PartEntry->IsPartitioned && PartEntry->Volume) || CanBePartitioned)
{
// ASSERT(PartEntry != PartEntry->DiskEntry->ExtendedPartition);
ASSERT(!IsContainerPartition(PartEntry->PartitionType));
+48 -3
View File
@@ -447,8 +447,16 @@ TypeDlgProc(
SetDlgItemFont(hwndDlg, IDC_INSTALL, pSetupData->hBoldFont, TRUE);
SetDlgItemFont(hwndDlg, IDC_UPDATE, pSetupData->hBoldFont, TRUE);
/* Check the "Install" radio button */
CheckDlgButton(hwndDlg, IDC_INSTALL, BST_CHECKED);
/*
* Enable double-click handling with the BS_NOTIFY style for both options.
* Idea adapted from: https://devblogs.microsoft.com/oldnewthing/20050804-10/?p=34713
*/
{
HWND hRadio = GetDlgItem(hwndDlg, IDC_INSTALL);
SetWindowLongPtrW(hRadio, GWL_STYLE, GetWindowLongPtrW(hRadio, GWL_STYLE) | BS_NOTIFY);
hRadio = GetDlgItem(hwndDlg, IDC_UPDATE);
SetWindowLongPtrW(hRadio, GWL_STYLE, GetWindowLongPtrW(hRadio, GWL_STYLE) | BS_NOTIFY);
}
/*
* Enable the "Update" radio button and text only if we have
@@ -466,11 +474,28 @@ TypeDlgProc(
EnableDlgItem(hwndDlg, IDC_UPDATETEXT, FALSE);
}
/* Ensure "Install ReactOS" is initially focused */
/* Check the "Install ReactOS" radio button and ensure it is initially focused */
CheckRadioButton(hwndDlg, IDC_INSTALL, IDC_UPDATE, IDC_INSTALL);
SetFocus(GetDlgItem(hwndDlg, IDC_INSTALL));
return FALSE;
}
case WM_COMMAND:
{
/*
* Go to the next page if the user double-clicked one of the options.
* Idea adapted from: https://devblogs.microsoft.com/oldnewthing/20050804-10/?p=34713
*/
if (HIWORD(wParam) == BN_DBLCLK) switch (LOWORD(wParam))
{
case IDC_INSTALL:
case IDC_UPDATE:
PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT);
return TRUE;
}
break;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
@@ -478,8 +503,19 @@ TypeDlgProc(
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
/* Ensure the "Install ReactOS" radio button is checked if we don't have
* a selected installation (default case), which can also happen if the
* user clicked on the "Do not upgrade" button on the Upgrade/Repair
* selection page, then went back here. */
if (!pSetupData->CurrentInstallation)
CheckRadioButton(hwndDlg, IDC_INSTALL, IDC_UPDATE, IDC_INSTALL);
else
CheckRadioButton(hwndDlg, IDC_INSTALL, IDC_UPDATE, IDC_UPDATE);
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;
}
case PSN_QUERYCANCEL:
{
@@ -866,6 +902,15 @@ UpgradeRepairDlgProc(
{
LPNMHDR lpnm = (LPNMHDR)lParam;
if (lpnm->idFrom == IDC_NTOSLIST && lpnm->code == NM_DBLCLK)
{
/* Go to the next page if the user double-clicked on an installation */
LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
if (pnmv->iItem != -1)
PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT);
break;
}
if (lpnm->idFrom == IDC_NTOSLIST && lpnm->code == LVN_ITEMCHANGED)
{
LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;