Skip to content

Commit

Permalink
fixed folder selection in file dialog and added same-value-selected e…
Browse files Browse the repository at this point in the history
…vent to list
  • Loading branch information
RonenNess committed Sep 29, 2023
1 parent abe3d76 commit 641eca2
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 24 deletions.
33 changes: 25 additions & 8 deletions GeonBit.UI.Examples/GeonBitUI_Examples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1083,16 +1083,33 @@ Note that in order to use clipping and scrollbar with Panels you need to set the
panel.AddChild(new HorizontalLine());
panel.AddChild(new Paragraph("GeonBit.UI also provide file dialogs. For example, save file dialog: \n"));

// add create form button
var btn = panel.AddChild(new Button(@"Open Save File Dialog"));
btn.OnClick += (Entity ent) =>
// add save file button
{
Utils.MessageBox.OpenSaveFileDialog("", (Utils.FileDialogResponse res) =>
var btn = panel.AddChild(new Button(@"Open Save File Dialog"));
btn.OnClick += (Entity ent) =>
{
Utils.MessageBox.ShowMsgBox("File Selected!", $"Selected file: '{res.FullPath}'.\n\nIn this example we just show a message box, in a real project we would use this path to save the file.");
return true;
}, message: "It won't actually save anything so don't worry about picking existing files!");
};
Utils.MessageBox.OpenSaveFileDialog("", (Utils.FileDialogResponse res) =>
{
Utils.MessageBox.ShowMsgBox("File Selected!", $"Selected file: '{res.FullPath}'.\n\nIn this example we just show a message box, in a real project we would use this path to save the file.");
return true;
}, message: "It won't actually save anything so don't worry about picking existing files.");
};
}

panel.AddChild(new Paragraph("And click below to open load file dialog: \n"));

// add load file button
{
var btn = panel.AddChild(new Button(@"Open Load File Dialog"));
btn.OnClick += (Entity ent) =>
{
Utils.MessageBox.OpenLoadFileDialog("", (Utils.FileDialogResponse res) =>
{
Utils.MessageBox.ShowMsgBox("File Selected!", $"Selected file: '{res.FullPath}'.\n\nIn this example we just show a message box, in a real project we would use this path to load the file.");
return true;
}, message: "It won't actually load anything so don't worry about picking any file.");
};
}
}

// example: top menu
Expand Down
2 changes: 1 addition & 1 deletion GeonBit.UI.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ It provide all the basic elements required to make a game / editor UI, and comes
<projectUrl>https://github.com/RonenNess/GeonBit.UI</projectUrl>
<iconUrl>https://raw.githubusercontent.com/RonenNess/GeonBit.UI/master/assets/img/nuget_icon.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes>Added features and small refactoring.</releaseNotes>
<releaseNotes>Added file dialogs, icons to lists, and did minor refactoring.</releaseNotes>
<description>UI system for MonoGame projects.</description>
<tags>monogame ui gui hud user-interface gamedev</tags>
<dependencies>
Expand Down
57 changes: 49 additions & 8 deletions GeonBit.UI/Source/Entities/SelectList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using GeonBit.UI.Utils;
using System;

namespace GeonBit.UI.Entities
{
Expand Down Expand Up @@ -62,10 +63,14 @@ static SelectList()
/// <summary>Scale items in list.</summary>
public float ItemsScale = 1f;

/// <summary>Special callback to execute when list size changes.</summary>
/// <summary>Invoked when list size changes.</summary>
[System.Xml.Serialization.XmlIgnore]
public EventCallback OnListChange = null;

/// <summary>Invoked when the user select the same value in the list again.</summary>
[System.Xml.Serialization.XmlIgnore]
public EventCallback OnSameValueSelected = null;

/// <summary>
/// If icons are set, this factor will scale them.
/// </summary>
Expand Down Expand Up @@ -253,7 +258,7 @@ public void ClearIcons()
/// </summary>
/// <param name="texturePath">Icon texture path, under theme folder. Set to null to remove icons.</param>
/// <param name="index">Item index to attach icon to.</param>
public void SetIcon(string? texturePath, int index)
public void SetIcon(string texturePath, int index)
{
if (texturePath == null)
{
Expand All @@ -270,7 +275,7 @@ public void SetIcon(string? texturePath, int index)
/// </summary>
/// <param name="texturePath">Icon texture path, under theme folder. Set to null to remove icons.</param>
/// <param name="itemText">Item text to attach icon to.</param>
public void SetIcon(string? texturePath, string itemText)
public void SetIcon(string texturePath, string itemText)
{
var index = 0;
foreach (var item in _valuesList)
Expand Down Expand Up @@ -614,7 +619,19 @@ public bool HasSelectedValue
protected void Select(string value)
{
// value not changed? skip
if (!AllowReselectValue && value == _value) { return; }
if (!AllowReselectValue && value == _value)
{
// invoke select same value event
if ((value == _value) && (value != null))
{
OnSameValueSelected?.Invoke(this);
}
// stop here
return;
}

// store previous value
var prevValue = _value;

// special case - value is null
if (value == null)
Expand All @@ -630,7 +647,7 @@ protected void Select(string value)
if (_index == -1)
{
_value = null;
if (UserInterface.Active.SilentSoftErrors) return;
if (UserInterface.Active.SilentSoftErrors) { return; }
throw new Exceptions.NotFoundException("Value to set not found in list!");
}

Expand All @@ -639,6 +656,12 @@ protected void Select(string value)

// call on-value-change event
DoOnValueChange();

// trigger same-value selected event
if ((value == prevValue) && (value != null))
{
OnSameValueSelected?.Invoke(this);
}
}

/// <summary>
Expand Down Expand Up @@ -678,13 +701,25 @@ protected void Select(int index, bool relativeToScrollbar = false)
index += _scrollbar.Value;
}

// store previous index. we use it to test same-selection.
var prevIndex = _index;

// index not changed? skip
if (!AllowReselectValue && index == _index) { return; }
if (!AllowReselectValue && index == _index)
{
// invoke select same value event
if (index == prevIndex)
{
OnSameValueSelected?.Invoke(this);
}
// stop here
return;
}

// make sure legal index
if (index >= -1 && index >= _valuesList.Count)
if ((index >= -1) && (index >= _valuesList.Count))
{
if (UserInterface.Active.SilentSoftErrors) return;
if (UserInterface.Active.SilentSoftErrors) { return; }
throw new Exceptions.NotFoundException("Invalid list index to select!");
}

Expand All @@ -694,6 +729,12 @@ protected void Select(int index, bool relativeToScrollbar = false)

// call on-value-change event
DoOnValueChange();

// invoke select same value event
if (index == prevIndex)
{
OnSameValueSelected?.Invoke(this);
}
}

/// <summary>
Expand Down
61 changes: 54 additions & 7 deletions GeonBit.UI/Source/Utils/MessageBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ public static MessageBoxHandle OpenSaveFileDialog(string path, Func<FileDialogRe
filenameInput.Validators.Add(new FilenameValidator(true));
filenameInput.Offset = new Vector2(0, -5);

// if we must pick existing files, hide the file name input
if (options.HasFlag(FileDialogOptions.MustSelectExistingFile))
{
filenameInput.Visible = false;
}

// create starting files list
void UpdateFilesList()
{
Expand All @@ -315,6 +321,12 @@ void UpdateFilesList()
// update full path label
fullPathLabel.Text = Path.GetFullPath(currPath);

// if we must select existing file, reset selection when change folder
if (options.HasFlag(FileDialogOptions.MustSelectExistingFile))
{
filenameInput.Value = string.Empty;
}

// clear previous list
filesList.ClearItems();

Expand Down Expand Up @@ -356,13 +368,12 @@ void UpdateFilesList()
}

// click on files list - check if enter or exit folder
int prevSelectedIndex = -1;
if (options.HasFlag(FileDialogOptions.AllowEnterFolders))
{
filesList.OnClick = (Entity entity) =>
filesList.OnSameValueSelected = (Entity entity) =>
{
// on second click enter folder
if ((filesList.SelectedIndex == prevSelectedIndex) && (filesList.SelectedValue != null))
if (filesList.SelectedValue != null)
{
// previous path to check if changed path
var prevPath = currPath;
Expand All @@ -386,7 +397,6 @@ void UpdateFilesList()
if (prevPath != currPath)
{
UpdateFilesList();
prevSelectedIndex = -1;
}
}
};
Expand All @@ -400,9 +410,6 @@ void UpdateFilesList()
{
filenameInput.Value = filesList.SelectedValue;
}

// to detect double click
prevSelectedIndex = filesList.SelectedIndex;
};

// return relative and full selected path
Expand Down Expand Up @@ -472,14 +479,24 @@ void UpdateFilesList()
var saveBtn = handle.Buttons[0];
saveBtn.BeforeDraw = (Entity entity) =>
{
// check if got any file name
saveBtn.Enabled = !string.IsNullOrEmpty(filenameInput.Value);

// if got filename, do advance checks
if (saveBtn.Enabled)
{
// get full path and check if override file
var paths = GetSelectedAndFullPath();
if (!options.HasFlag(FileDialogOptions.AllowOverride) && File.Exists(paths.Item2))
{
saveBtn.Enabled = false;
}

// check if a folder is selected
if (!options.HasFlag(FileDialogOptions.CanPickFolders) && Directory.Exists(paths.Item2))
{
saveBtn.Enabled = false;
}
}
};

Expand All @@ -489,6 +506,26 @@ void UpdateFilesList()
// return the handle
return handle;
}

/// <summary>
/// Open a dialog to select file for loading.
/// </summary>
/// <param name="path">Path to start dialog in.</param>
/// <param name="onSelected">Callback to trigger when a file was selected. Return true to close dialog, false to keep it opened.</param>
/// <param name="onCancel">Callback to trigger when the user hit cancel.</param>
/// <param name="options">File dialog flags.</param>
/// <param name="filterFiles">Optional method to filter file names. Return false to hide files.</param>
/// <param name="filterFolders">Optional method to filter folder names. Return false to hide folders.</param>
/// <param name="title">File dialog title.</param>
/// <param name="message">Optional message to show above files.</param>
/// <param name="loadButtonTxt">String to show on the load file button.</param>
/// <param name="cancelButtonTxt">String to show on the cancel button.</param>
/// <returns>Message box handle.</returns>
public static MessageBoxHandle OpenLoadFileDialog(string path, Func<FileDialogResponse, bool> onSelected, Action onCancel = null!, FileDialogOptions options = FileDialogOptions.Default, Func<string, bool> filterFiles = null!, Func<string, bool> filterFolders = null!, string title = "Open File..", string message = null!, string loadButtonTxt = "Open File", string cancelButtonTxt = "Cancel")
{
options |= FileDialogOptions.MustSelectExistingFile;
return OpenSaveFileDialog(path, onSelected, onCancel, options, filterFiles, filterFolders, title, message, loadButtonTxt, cancelButtonTxt, null);
}
}

/// <summary>
Expand Down Expand Up @@ -543,6 +580,16 @@ public enum FileDialogOptions
/// </summary>
ShowDirectoryPath = 1 << 4,

/// <summary>
/// Will only allow picking up existing files.
/// </summary>
MustSelectExistingFile = 1 << 5,

/// <summary>
/// Can select folders and not just files.
/// </summary>
CanPickFolders = 1 << 6,

/// <summary>
/// Default file dialog options.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,7 @@ For older MonoGame versions, see [tag 2.1.0.0](https://github.com/RonenNess/Geon
- Added built-in files dialog (save / load file).
- Renamed and rearranged text validators + added file name validator.
- Added icons support to Lists and Drop Down entities.
- Added 'OnSameValueSelected' event to lists.

IMPORTANT MIGRATION NOTICE!
If you want to use the new files dialog, you must include the new textures that were added to theme: `textures/file_icon.png` and `textures/folder_icon.png`.
Expand Down

0 comments on commit 641eca2

Please sign in to comment.