Refactor size and position logic

Fix issue that custom location was always detected being out of bounds
This commit is contained in:
Peter Kirmeier 2023-04-22 21:04:34 +02:00
parent 3464e3e4b9
commit 55c01e44b8
2 changed files with 150 additions and 130 deletions

View file

@ -60,88 +60,6 @@ namespace SystemTrayMenu.Business
workerMainMenu.WorkerSupportsCancellation = true;
workerMainMenu.DoWork += LoadMenu;
workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted;
void LoadMainMenuCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
keyboardInput.ResetSelectedByKey();
LoadStopped?.Invoke();
if (e.Result == null)
{
// The main menu gets loaded again
// Clean up menu status of previous one
ListView? dgvMainMenu = menus[0]?.GetDataGridView();
if (dgvMainMenu != null)
{
foreach (ListViewItemData item in dgvMainMenu.Items)
{
RowData rowDataToClear = item.data;
rowDataToClear.IsMenuOpen = false;
rowDataToClear.IsClicking = false;
rowDataToClear.IsSelected = false;
rowDataToClear.IsContextMenuOpen = false;
}
RefreshSelection(dgvMainMenu);
}
if (Settings.Default.AppearAtMouseLocation)
{
Menu? menu = menus[0];
if (menu != null)
{
menu.RowDataParent = null;
}
}
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
}
else
{
// First time the main menu gets loaded
MenuData menuData = (MenuData)e.Result;
switch (menuData.DirectoryState)
{
case MenuDataDirectoryState.Valid:
if (IconReader.IsPreloading)
{
workerMainMenu.DoWork -= LoadMenu;
workerMainMenu.CancelAsync();
Create(menuData, Config.Path); // Level 0 Main Menu
IconReader.IsPreloading = false;
if (showMenuAfterMainPreload)
{
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
}
}
else
{
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
}
break;
case MenuDataDirectoryState.Empty:
MessageBox.Show(Translator.GetText("Your root directory for the app does not exist or is empty! Change the root directory or put some files, directories or shortcuts into the root directory."));
OpenFolder();
Config.SetFolderByUser();
AppRestart.ByConfigChange();
break;
case MenuDataDirectoryState.NoAccess:
MessageBox.Show(Translator.GetText("You have no access to the root directory of the app. Grant access to the directory or change the root directory."));
OpenFolder();
Config.SetFolderByUser();
AppRestart.ByConfigChange();
break;
case MenuDataDirectoryState.Undefined:
Log.Info($"{nameof(MenuDataDirectoryState)}.{nameof(MenuDataDirectoryState.Undefined)}");
break;
default:
break;
}
}
openCloseState = OpenCloseState.Default;
}
waitToOpenMenu.StopLoadMenu += WaitToOpenMenu_StopLoadMenu;
void WaitToOpenMenu_StopLoadMenu()
@ -446,11 +364,12 @@ namespace SystemTrayMenu.Business
}
}
private static void LoadMenu(object? senderDoWork, DoWorkEventArgs eDoWork)
private static void LoadMenu(object? sender, DoWorkEventArgs eDoWork)
{
string? path;
int level = 0;
BackgroundWorker? workerSelf = sender as BackgroundWorker;
RowData? rowData = eDoWork.Argument as RowData;
string? path;
int level;
if (rowData != null)
{
path = rowData.ResolvedPath;
@ -464,13 +383,99 @@ namespace SystemTrayMenu.Business
else
{
path = Config.Path;
level = 0;
}
MenuData menuData = new(level, rowData);
DirectoryHelpers.DiscoverItems((BackgroundWorker?)senderDoWork, path, ref menuData);
DirectoryHelpers.DiscoverItems(workerSelf, path, ref menuData);
if (menuData.DirectoryState != MenuDataDirectoryState.Undefined &&
workerSelf != null && level == 0)
{
// After success of MainMenu loading: never run again
workerSelf.DoWork -= LoadMenu;
}
eDoWork.Result = menuData;
}
private void LoadMainMenuCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
keyboardInput.ResetSelectedByKey();
LoadStopped?.Invoke();
if (e.Result == null)
{
Menu? menu = menus[0];
if (menu != null)
{
// The main menu gets loaded again
// Clean up menu status of previous one
ListView? dgvMainMenu = menu.GetDataGridView();
if (dgvMainMenu != null)
{
foreach (ListViewItemData item in dgvMainMenu.Items)
{
RowData rowDataToClear = item.data;
rowDataToClear.IsMenuOpen = false;
rowDataToClear.IsClicking = false;
rowDataToClear.IsSelected = false;
rowDataToClear.IsContextMenuOpen = false;
}
RefreshSelection(dgvMainMenu);
}
menu.RelocateOnNextShow = true;
}
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
}
else
{
// First time the main menu gets loaded
MenuData menuData = (MenuData)e.Result;
switch (menuData.DirectoryState)
{
case MenuDataDirectoryState.Valid:
if (IconReader.IsPreloading)
{
Create(menuData, Config.Path); // Level 0 Main Menu
IconReader.IsPreloading = false;
if (showMenuAfterMainPreload)
{
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
}
}
else
{
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
}
break;
case MenuDataDirectoryState.Empty:
MessageBox.Show(Translator.GetText("Your root directory for the app does not exist or is empty! Change the root directory or put some files, directories or shortcuts into the root directory."));
OpenFolder();
Config.SetFolderByUser();
AppRestart.ByConfigChange();
break;
case MenuDataDirectoryState.NoAccess:
MessageBox.Show(Translator.GetText("You have no access to the root directory of the app. Grant access to the directory or change the root directory."));
OpenFolder();
Config.SetFolderByUser();
AppRestart.ByConfigChange();
break;
case MenuDataDirectoryState.Undefined:
Log.Info($"{nameof(MenuDataDirectoryState)}.{nameof(MenuDataDirectoryState.Undefined)}");
break;
default:
break;
}
}
openCloseState = OpenCloseState.Default;
}
private bool IsActive()
{
bool IsShellContextMenuOpen()
@ -860,8 +865,7 @@ namespace SystemTrayMenu.Business
Menu? menu = menus[0];
if (menu != null)
{
// TODO: What does this do???
menu.RowDataParent = null;
menu.RelocateOnNextShow = true;
}
}
});
@ -958,7 +962,7 @@ namespace SystemTrayMenu.Business
Settings.Default.CustomLocationX,
Settings.Default.CustomLocationY));
useCustomLocation = !screenBounds.Contains(
useCustomLocation = screenBounds.Contains(
new Point(Settings.Default.CustomLocationX, Settings.Default.CustomLocationY));
}
else

View file

@ -323,14 +323,17 @@ namespace SystemTrayMenu.UserInterface
BottomLeft,
BottomRight,
TopRight,
Point,
}
public System.Drawing.Point Location => new ((int)Left, (int)Top); // TODO WPF Replace Forms wrapper
public Point Location => new (Left, Top); // TODO WPF Replace Forms wrapper
internal int Level { get; set; }
internal RowData? RowDataParent { get; set; }
internal bool RelocateOnNextShow { get; set; } = true;
internal bool IsClosed { get; private set; } = false;
internal bool IsUsable => Visibility == Visibility.Visible && !isFading && !IsClosed;
@ -542,86 +545,99 @@ namespace SystemTrayMenu.UserInterface
StartLocation startLocation,
bool useCustomLocation)
{
Point originLocation = new(0, 0);
// Update the height and width
AdjustDataGridViewHeight(menuPredecessor, bounds.Height);
AdjustDataGridViewWidth();
bool changeDirectionWhenOutOfBounds = true;
if (Level > 0)
{
if (menuPredecessor == null)
{
// should never happen
return;
}
// Sub Menu location depends on the location of its predecessor
startLocation = StartLocation.Predecessor;
originLocation = menuPredecessor.Location;
}
else if (useCustomLocation)
{
// Do not adjust location again because Cursor.Postion changed
if (RowDataParent != null)
if (!RelocateOnNextShow)
{
return;
}
// Use this menu as predecessor and overwrite location with CustomLocation
menuPredecessor = this;
RowDataParent = new RowData();
Left = Settings.Default.CustomLocationX;
Top = Settings.Default.CustomLocationY;
directionToRight = true;
startLocation = StartLocation.Predecessor;
changeDirectionWhenOutOfBounds = false;
RelocateOnNextShow = false;
originLocation = new(Settings.Default.CustomLocationX, Settings.Default.CustomLocationY);
startLocation = StartLocation.Point;
}
else if (Settings.Default.AppearAtMouseLocation)
{
// Do not adjust location again because Cursor.Postion changed
if (RowDataParent != null)
if (!RelocateOnNextShow)
{
return;
}
// Use this menu as predecessor and overwrite location with Cursor.Postion
menuPredecessor = this;
RowDataParent = new RowData();
var position = Mouse.GetPosition(this);
Left = position.X;
Top = position.Y - labelTitle.Height;
directionToRight = true;
startLocation = StartLocation.Predecessor;
changeDirectionWhenOutOfBounds = false;
RelocateOnNextShow = false;
originLocation = Mouse.GetPosition(this);
originLocation.Y -= labelTitle.Height;
startLocation = StartLocation.Point;
}
if (IsLoaded)
{
AdjustWindowPositionInternal();
AdjustWindowPositionInternal(originLocation);
}
else
{
// Layout cannot be calculated during loading, postpone this event
Loaded += (_, _) => AdjustWindowPositionInternal();
Loaded += (_, _) => AdjustWindowPositionInternal(originLocation);
}
void AdjustWindowPositionInternal()
void AdjustWindowPositionInternal(Point originLocation)
{
RowData? trigger;
// Make sure we have latest values of own window size
UpdateLayout();
// Prepare parameters
if (startLocation == StartLocation.Predecessor && menuPredecessor != null)
{
directionToRight = menuPredecessor.directionToRight; // try keeping same direction from predecessor
trigger = RowDataParent;
}
else
{
// Use own menu as predecessor for calculations (Left and Top were set beforehand)
menuPredecessor = this;
directionToRight = true; // use right as default direction
trigger = new();
}
// Calculate X position
double x;
switch (startLocation)
{
case StartLocation.Point:
case StartLocation.Predecessor:
double scaling = Math.Round(Scaling.Factor, 0, MidpointRounding.AwayFromZero);
directionToRight = menuPredecessor!.directionToRight; // try keeping same direction
if (directionToRight)
{
x = menuPredecessor.Location.X + menuPredecessor.Width - scaling;
x = originLocation.X + menuPredecessor.Width - scaling;
if (changeDirectionWhenOutOfBounds &&
// Change direction when out of bounds (predecessor only)
if (startLocation == StartLocation.Predecessor &&
bounds.X + bounds.Width <= x + Width - scaling)
{
x = menuPredecessor.Location.X - Width + scaling;
x = originLocation.X - Width + scaling;
if (x < bounds.X &&
menuPredecessor.Location.X + menuPredecessor.Width < bounds.X + bounds.Width &&
bounds.X + (bounds.Width / 2) > menuPredecessor.Location.X + (Width / 2))
originLocation.X + menuPredecessor.Width < bounds.X + bounds.Width &&
bounds.X + (bounds.Width / 2) > originLocation.X + (Width / 2))
{
x = bounds.X + bounds.Width - Width + scaling;
}
@ -638,15 +654,16 @@ namespace SystemTrayMenu.UserInterface
}
else
{
x = menuPredecessor.Location.X - Width + scaling;
x = originLocation.X - Width + scaling;
if (changeDirectionWhenOutOfBounds &&
// Change direction when out of bounds (predecessor only)
if (startLocation == StartLocation.Predecessor &&
x < bounds.X)
{
x = menuPredecessor.Location.X + menuPredecessor.Width - scaling;
x = originLocation.X + menuPredecessor.Width - scaling;
if (x + Width > bounds.X + bounds.Width &&
menuPredecessor.Location.X > bounds.X &&
bounds.X + (bounds.Width / 2) < menuPredecessor.Location.X + (Width / 2))
originLocation.X > bounds.X &&
bounds.X + (bounds.Width / 2) < originLocation.X + (Width / 2))
{
x = bounds.X;
}
@ -699,13 +716,12 @@ namespace SystemTrayMenu.UserInterface
double y;
switch (startLocation)
{
case StartLocation.Point:
case StartLocation.Predecessor:
RowData? trigger = RowDataParent;
ListView dgv = menuPredecessor!.GetDataGridView()!;
// Set position on same height as the selected row from predecessor
y = menuPredecessor.Location.Y;
y = originLocation.Y;
if (trigger != null && dgv.Items.Count > trigger.RowIndex)
{
// When item is not found, it might be invalidated due to resizing or moving