mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-10-03 10:36:30 +13:00
Refactor size and position logic
Fix issue that custom location was always detected being out of bounds
This commit is contained in:
parent
3464e3e4b9
commit
55c01e44b8
2 changed files with 150 additions and 130 deletions
|
@ -60,88 +60,6 @@ namespace SystemTrayMenu.Business
|
||||||
workerMainMenu.WorkerSupportsCancellation = true;
|
workerMainMenu.WorkerSupportsCancellation = true;
|
||||||
workerMainMenu.DoWork += LoadMenu;
|
workerMainMenu.DoWork += LoadMenu;
|
||||||
workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted;
|
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;
|
waitToOpenMenu.StopLoadMenu += WaitToOpenMenu_StopLoadMenu;
|
||||||
void 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;
|
BackgroundWorker? workerSelf = sender as BackgroundWorker;
|
||||||
int level = 0;
|
|
||||||
RowData? rowData = eDoWork.Argument as RowData;
|
RowData? rowData = eDoWork.Argument as RowData;
|
||||||
|
string? path;
|
||||||
|
int level;
|
||||||
if (rowData != null)
|
if (rowData != null)
|
||||||
{
|
{
|
||||||
path = rowData.ResolvedPath;
|
path = rowData.ResolvedPath;
|
||||||
|
@ -464,13 +383,99 @@ namespace SystemTrayMenu.Business
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
path = Config.Path;
|
path = Config.Path;
|
||||||
|
level = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuData menuData = new(level, rowData);
|
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;
|
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()
|
private bool IsActive()
|
||||||
{
|
{
|
||||||
bool IsShellContextMenuOpen()
|
bool IsShellContextMenuOpen()
|
||||||
|
@ -860,8 +865,7 @@ namespace SystemTrayMenu.Business
|
||||||
Menu? menu = menus[0];
|
Menu? menu = menus[0];
|
||||||
if (menu != null)
|
if (menu != null)
|
||||||
{
|
{
|
||||||
// TODO: What does this do???
|
menu.RelocateOnNextShow = true;
|
||||||
menu.RowDataParent = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -958,7 +962,7 @@ namespace SystemTrayMenu.Business
|
||||||
Settings.Default.CustomLocationX,
|
Settings.Default.CustomLocationX,
|
||||||
Settings.Default.CustomLocationY));
|
Settings.Default.CustomLocationY));
|
||||||
|
|
||||||
useCustomLocation = !screenBounds.Contains(
|
useCustomLocation = screenBounds.Contains(
|
||||||
new Point(Settings.Default.CustomLocationX, Settings.Default.CustomLocationY));
|
new Point(Settings.Default.CustomLocationX, Settings.Default.CustomLocationY));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -323,14 +323,17 @@ namespace SystemTrayMenu.UserInterface
|
||||||
BottomLeft,
|
BottomLeft,
|
||||||
BottomRight,
|
BottomRight,
|
||||||
TopRight,
|
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 int Level { get; set; }
|
||||||
|
|
||||||
internal RowData? RowDataParent { get; set; }
|
internal RowData? RowDataParent { get; set; }
|
||||||
|
|
||||||
|
internal bool RelocateOnNextShow { get; set; } = true;
|
||||||
|
|
||||||
internal bool IsClosed { get; private set; } = false;
|
internal bool IsClosed { get; private set; } = false;
|
||||||
|
|
||||||
internal bool IsUsable => Visibility == Visibility.Visible && !isFading && !IsClosed;
|
internal bool IsUsable => Visibility == Visibility.Visible && !isFading && !IsClosed;
|
||||||
|
@ -542,86 +545,99 @@ namespace SystemTrayMenu.UserInterface
|
||||||
StartLocation startLocation,
|
StartLocation startLocation,
|
||||||
bool useCustomLocation)
|
bool useCustomLocation)
|
||||||
{
|
{
|
||||||
|
Point originLocation = new(0, 0);
|
||||||
|
|
||||||
// Update the height and width
|
// Update the height and width
|
||||||
AdjustDataGridViewHeight(menuPredecessor, bounds.Height);
|
AdjustDataGridViewHeight(menuPredecessor, bounds.Height);
|
||||||
AdjustDataGridViewWidth();
|
AdjustDataGridViewWidth();
|
||||||
|
|
||||||
bool changeDirectionWhenOutOfBounds = true;
|
|
||||||
|
|
||||||
if (Level > 0)
|
if (Level > 0)
|
||||||
{
|
{
|
||||||
|
if (menuPredecessor == null)
|
||||||
|
{
|
||||||
|
// should never happen
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Sub Menu location depends on the location of its predecessor
|
// Sub Menu location depends on the location of its predecessor
|
||||||
startLocation = StartLocation.Predecessor;
|
startLocation = StartLocation.Predecessor;
|
||||||
|
originLocation = menuPredecessor.Location;
|
||||||
}
|
}
|
||||||
else if (useCustomLocation)
|
else if (useCustomLocation)
|
||||||
{
|
{
|
||||||
// Do not adjust location again because Cursor.Postion changed
|
if (!RelocateOnNextShow)
|
||||||
if (RowDataParent != null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use this menu as predecessor and overwrite location with CustomLocation
|
RelocateOnNextShow = false;
|
||||||
menuPredecessor = this;
|
originLocation = new(Settings.Default.CustomLocationX, Settings.Default.CustomLocationY);
|
||||||
RowDataParent = new RowData();
|
startLocation = StartLocation.Point;
|
||||||
Left = Settings.Default.CustomLocationX;
|
|
||||||
Top = Settings.Default.CustomLocationY;
|
|
||||||
directionToRight = true;
|
|
||||||
startLocation = StartLocation.Predecessor;
|
|
||||||
changeDirectionWhenOutOfBounds = false;
|
|
||||||
}
|
}
|
||||||
else if (Settings.Default.AppearAtMouseLocation)
|
else if (Settings.Default.AppearAtMouseLocation)
|
||||||
{
|
{
|
||||||
// Do not adjust location again because Cursor.Postion changed
|
if (!RelocateOnNextShow)
|
||||||
if (RowDataParent != null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use this menu as predecessor and overwrite location with Cursor.Postion
|
RelocateOnNextShow = false;
|
||||||
menuPredecessor = this;
|
originLocation = Mouse.GetPosition(this);
|
||||||
RowDataParent = new RowData();
|
originLocation.Y -= labelTitle.Height;
|
||||||
var position = Mouse.GetPosition(this);
|
startLocation = StartLocation.Point;
|
||||||
Left = position.X;
|
|
||||||
Top = position.Y - labelTitle.Height;
|
|
||||||
directionToRight = true;
|
|
||||||
startLocation = StartLocation.Predecessor;
|
|
||||||
changeDirectionWhenOutOfBounds = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsLoaded)
|
if (IsLoaded)
|
||||||
{
|
{
|
||||||
AdjustWindowPositionInternal();
|
AdjustWindowPositionInternal(originLocation);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Layout cannot be calculated during loading, postpone this event
|
// 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
|
// Make sure we have latest values of own window size
|
||||||
UpdateLayout();
|
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
|
// Calculate X position
|
||||||
double x;
|
double x;
|
||||||
switch (startLocation)
|
switch (startLocation)
|
||||||
{
|
{
|
||||||
|
case StartLocation.Point:
|
||||||
case StartLocation.Predecessor:
|
case StartLocation.Predecessor:
|
||||||
double scaling = Math.Round(Scaling.Factor, 0, MidpointRounding.AwayFromZero);
|
double scaling = Math.Round(Scaling.Factor, 0, MidpointRounding.AwayFromZero);
|
||||||
directionToRight = menuPredecessor!.directionToRight; // try keeping same direction
|
|
||||||
if (directionToRight)
|
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)
|
bounds.X + bounds.Width <= x + Width - scaling)
|
||||||
{
|
{
|
||||||
x = menuPredecessor.Location.X - Width + scaling;
|
x = originLocation.X - Width + scaling;
|
||||||
if (x < bounds.X &&
|
if (x < bounds.X &&
|
||||||
menuPredecessor.Location.X + menuPredecessor.Width < bounds.X + bounds.Width &&
|
originLocation.X + menuPredecessor.Width < bounds.X + bounds.Width &&
|
||||||
bounds.X + (bounds.Width / 2) > menuPredecessor.Location.X + (Width / 2))
|
bounds.X + (bounds.Width / 2) > originLocation.X + (Width / 2))
|
||||||
{
|
{
|
||||||
x = bounds.X + bounds.Width - Width + scaling;
|
x = bounds.X + bounds.Width - Width + scaling;
|
||||||
}
|
}
|
||||||
|
@ -638,15 +654,16 @@ namespace SystemTrayMenu.UserInterface
|
||||||
}
|
}
|
||||||
else
|
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 < bounds.X)
|
||||||
{
|
{
|
||||||
x = menuPredecessor.Location.X + menuPredecessor.Width - scaling;
|
x = originLocation.X + menuPredecessor.Width - scaling;
|
||||||
if (x + Width > bounds.X + bounds.Width &&
|
if (x + Width > bounds.X + bounds.Width &&
|
||||||
menuPredecessor.Location.X > bounds.X &&
|
originLocation.X > bounds.X &&
|
||||||
bounds.X + (bounds.Width / 2) < menuPredecessor.Location.X + (Width / 2))
|
bounds.X + (bounds.Width / 2) < originLocation.X + (Width / 2))
|
||||||
{
|
{
|
||||||
x = bounds.X;
|
x = bounds.X;
|
||||||
}
|
}
|
||||||
|
@ -699,13 +716,12 @@ namespace SystemTrayMenu.UserInterface
|
||||||
double y;
|
double y;
|
||||||
switch (startLocation)
|
switch (startLocation)
|
||||||
{
|
{
|
||||||
|
case StartLocation.Point:
|
||||||
case StartLocation.Predecessor:
|
case StartLocation.Predecessor:
|
||||||
|
|
||||||
RowData? trigger = RowDataParent;
|
|
||||||
ListView dgv = menuPredecessor!.GetDataGridView()!;
|
ListView dgv = menuPredecessor!.GetDataGridView()!;
|
||||||
|
|
||||||
// Set position on same height as the selected row from predecessor
|
// 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)
|
if (trigger != null && dgv.Items.Count > trigger.RowIndex)
|
||||||
{
|
{
|
||||||
// When item is not found, it might be invalidated due to resizing or moving
|
// When item is not found, it might be invalidated due to resizing or moving
|
||||||
|
|
Loading…
Reference in a new issue