Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derive User classes directly from IUser interface instead of the NullUser class. #2648

Merged
merged 4 commits into from
Jan 14, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 71 additions & 32 deletions Cmdline/ConsoleUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,44 @@

namespace CKAN.CmdLine
{
public class ConsoleUser : NullUser
/// <summary>
/// The commandline implementation of the IUser interface.
/// </summary>
public class ConsoleUser : IUser
{
/// <summary>
/// A logger for this class.
/// ONLY FOR INTERNAL USE!
/// </summary>
private static readonly ILog log = LogManager.GetLogger(typeof(ConsoleUser));

private bool m_Headless = false;
public ConsoleUser(bool headless)
/// <summary>
/// Initializes a new instance of the <see cref="T:CKAN.CmdLine.ConsoleUser"/> class.
/// </summary>
/// <param name="headless">If set to <c>true</c>, supress interactive dialogs like Yes/No-Dialog or SelectionDialog.</param>
public ConsoleUser (bool headless)
{
m_Headless = headless;
Headless = headless;
}

public override bool Headless
/// <summary>
/// Gets a value indicating whether this <see cref="T:CKAN.CmdLine.ConsoleUser"/> is headless.
/// </summary>
/// <value><c>true</c> if headless; otherwise, <c>false</c>.</value>
public bool Headless { get; }

/// <summary>
/// Ask the user for a yes or no input.
/// </summary>
/// <param name="question">Question.</param>
public bool RaiseYesNoDialog(string question)
{
get
{
return m_Headless;
}
}

protected override bool DisplayYesNoDialog(string message)
{
if (m_Headless)
if (Headless)
{
return true;
}

Console.Write("{0} [Y/n] ", message);
Console.Write("{0} [Y/n] ", question);
while (true)
{
var input = Console.In.ReadLine();
Expand Down Expand Up @@ -60,22 +72,20 @@ protected override bool DisplayYesNoDialog(string message)
}
}

protected override void DisplayMessage(string message, params object[] args)
{
Console.WriteLine(message, args);
}

protected override void DisplayError(string message, params object[] args)
{
Console.Error.WriteLine(message, args);
}

protected override int DisplaySelectionDialog(string message, params object[] args)
/// <summary>
/// Ask the user to select one of the elements of the array.
/// The output is index 0 based.
/// To supply a default option, make the first option an integer indicating the index of it.
/// </summary>
/// <returns>The selection dialog.</returns>
/// <param name="message">Message.</param>
/// <param name="args">Array of available options.</param>
public int RaiseSelectionDialog(string message, params object[] args)
{
const int return_cancel = -1;

// Check for the headless flag.
if (m_Headless)
if (Headless)
{
// Return that the user cancelled the selection process.
return return_cancel;
Expand Down Expand Up @@ -227,17 +237,33 @@ protected override int DisplaySelectionDialog(string message, params object[] ar
return result;
}

protected override void ReportProgress(string format, int percent)
/// <summary>
/// Write an error to the console.
/// </summary>
/// <param name="message">Message.</param>
/// <param name="args">Possible arguments to format the message.</param>
public void RaiseError(string message, params object[] args)
{
Console.Error.WriteLine(message, args);
}

/// <summary>
/// Write a progress message including the percentage to the console.
/// Rewrites the line, so the console is not cluttered by progress messages.
/// </summary>
/// <param name="message">Message.</param>
/// <param name="percent">Progress in percent.</param>
public void RaiseProgress(string message, int percent)
{
if (Regex.IsMatch(format, "download", RegexOptions.IgnoreCase))
if (Regex.IsMatch(message, "download", RegexOptions.IgnoreCase))
{
// In headless mode, only print a new message if the percent has changed,
// to reduce clutter in Jenkins for large downloads
if (!m_Headless || percent != previousPercent)
if (!Headless || percent != previousPercent)
{
// The \r at the front here causes download messages to *overwrite* each other.
Console.Write(
"\r{0} - {1}% ", format, percent);
"\r{0} - {1}% ", message, percent);
previousPercent = percent;
}
}
Expand All @@ -246,10 +272,23 @@ protected override void ReportProgress(string format, int percent)
// The percent looks weird on non-download messages.
// The leading newline makes sure we don't end up with a mess from previous
// download messages.
Console.Write("\r\n{0}", format);
Console.Write("\r\n{0}", message);
}
}

/// <summary>
/// Needed for <see cref="RaiseProgress(string, int)"/>
/// </summary>
private int previousPercent = -1;

/// <summary>
/// Writes a message to the console.
/// </summary>
/// <param name="message">Message.</param>
/// <param name="args">Arguments to format the message.</param>
public void RaiseMessage(string message, params object[] args)
{
Console.WriteLine(message, args);
}
}
}
4 changes: 2 additions & 2 deletions ConsoleUI/Toolkit/ConsoleScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ protected virtual string MenuTip()
/// </summary>
protected ConsolePopupMenu mainMenu = null;

// IUser
#region IUser

/// <summary>
/// Tell IUser clients that we have the ability to interact with the user
Expand Down Expand Up @@ -207,7 +207,7 @@ public void RaiseProgress(string message, int percent)
/// <param name="percent">Value from 0 to 100 representing task completion</param>
protected virtual void Progress(string message, int percent) { }

// End IUser
#endregion IUser

private void DrawSelectedHamburger()
{
Expand Down
20 changes: 18 additions & 2 deletions Core/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,15 @@ public void InstallList(ICollection<CkanModule> modules, RelationshipResolverOpt
}
}

bool ok = User.RaiseYesNoDialog("\r\nContinue?");
bool ok;
if (User.GetType().ToString() == "CKAN.GUIUser")
DasSkelett marked this conversation as resolved.
Show resolved Hide resolved
{
ok = true;
}
else
{
ok = User.RaiseYesNoDialog("\r\nContinue?");
}

if (!ok)
{
Expand Down Expand Up @@ -770,7 +778,15 @@ public void UninstallList(IEnumerable<string> mods)
User.RaiseMessage(" * {0} {1}", module.Module.name, module.Module.version);
}

bool ok = User.RaiseYesNoDialog("\r\nContinue?");
bool ok;
if (User.GetType().ToString() == "CKAN.GUIUser")
{
ok = true;
}
else
{
ok = User.RaiseYesNoDialog("\r\nContinue?");
}

if (!ok)
{
Expand Down
45 changes: 12 additions & 33 deletions Core/User.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Communicate with the user (status messages, yes/no questions, etc)
// This class will proxy to either the GUI or cmdline functionality.

namespace CKAN
{

/// <summary>
/// This interface holds all methods which communicate with the user in some way.
/// Every CKAN interface (GUI, cmdline, consoleUI) has an implementation of the IUser interface.
/// The implementations define HOW we interact with the user.
/// </summary>
public interface IUser
{
bool Headless { get; }
Expand All @@ -13,68 +14,46 @@ public interface IUser
void RaiseError(string message, params object[] args);

void RaiseProgress(string message, int percent);
void RaiseMessage(string message, params object[] url);
void RaiseMessage(string message, params object[] args);
}

//Can be used in tests to supress output or as a base class for other types of user.
//It supplies no op event handlers so that subclasses can avoid null checks.
/// <summary>
/// To be used in tests.
/// Supresses all output.
/// </summary>
public class NullUser : IUser
{
public static readonly IUser User = new NullUser();

public NullUser() { }

public virtual bool Headless
{
get { return false; }
}

protected virtual bool DisplayYesNoDialog(string message)
{
return true;
}

protected virtual int DisplaySelectionDialog(string message, params object[] args)
{
return 0;
}

protected virtual void DisplayError(string message, params object[] args)
{
}

protected virtual void ReportProgress(string format, int percent)
{
}

protected virtual void DisplayMessage(string message, params object[] args)
{
}

public bool RaiseYesNoDialog(string question)
{
return DisplayYesNoDialog(question);
return true;
}

public int RaiseSelectionDialog(string message, params object[] args)
{
return DisplaySelectionDialog(message, args);
return 0;
}

public void RaiseError(string message, params object[] args)
{
DisplayError(message, args);
}

public void RaiseProgress(string message, int percent)
{
ReportProgress(message, percent);
}

public void RaiseMessage(string message, params object[] args)
{
DisplayMessage(message, args);
}

}
}
3 changes: 3 additions & 0 deletions GUI/CKAN-GUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@
<DependentUpon>YesNoDialog.cs</DependentUpon>
</Compile>
<Compile Include="TransparentTextBox.cs" />
<Compile Include="SelectionDialog.cs" />
<Compile Include="SelectionDialog.Designer.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="AboutDialog.resx">
Expand Down Expand Up @@ -302,6 +304,7 @@
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<None Include="SelectionDialog.resx" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0">
Expand Down
4 changes: 2 additions & 2 deletions GUI/ChooseKSPInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private void AddNewButton_Click(object sender, EventArgs e)
}
catch (NotKSPDirKraken k)
{
GUI.user.displayError("Directory {0} is not valid KSP directory.",
GUI.user.RaiseError("Directory {0} is not valid KSP directory.",
new object[] { k.path });
return;
}
Expand Down Expand Up @@ -125,7 +125,7 @@ private void UseSelectedInstance()
}
catch (NotKSPDirKraken k)
{
GUI.user.displayError("Directory {0} is not valid KSP directory.",
GUI.user.RaiseError("Directory {0} is not valid KSP directory.",
new object[] { k.path });
}
}
Expand Down
Loading