Add support for toasts (#817)

Behavior can be configured in the "Toasts" tab in the preferences.
This commit is contained in:
biwa 2022-11-06 15:08:22 +01:00 committed by GitHub
parent 13e53ece53
commit 204982e5f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1210 additions and 39 deletions

View file

@ -34,6 +34,7 @@ namespace CodeImp.DoomBuilder.Actions
private readonly string title;
private readonly string description;
private readonly string category;
private readonly bool registertoast;
// Shortcut key
private int key;
@ -62,6 +63,7 @@ namespace CodeImp.DoomBuilder.Actions
public string Category { get { return category; } }
public string Title { get { return title; } }
public string Description { get { return description; } }
public bool RegisterToast { get { return registertoast; } }
public int ShortcutKey { get { return key; } }
public int ShortcutMask { get { return keymask; } }
public int DefaultShortcutKey { get { return defaultkey; } }
@ -88,6 +90,7 @@ namespace CodeImp.DoomBuilder.Actions
this.title = cfg.ReadSetting(shortname + ".title", "[" + name + "]");
this.category = cfg.ReadSetting(shortname + ".category", "");
this.description = cfg.ReadSetting(shortname + ".description", "");
this.registertoast = cfg.ReadSetting(shortname + ".registertoast", false);
this.allowkeys = cfg.ReadSetting(shortname + ".allowkeys", true);
this.allowmouse = cfg.ReadSetting(shortname + ".allowmouse", true);
this.allowscroll = cfg.ReadSetting(shortname + ".allowscroll", false);

View file

@ -204,6 +204,12 @@
<Compile Include="Controls\SidedefPartLightControl.Designer.cs">
<DependentUpon>SidedefPartLightControl.cs</DependentUpon>
</Compile>
<Compile Include="Controls\ToastControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Controls\ToastControl.Designer.cs">
<DependentUpon>ToastControl.cs</DependentUpon>
</Compile>
<Compile Include="Controls\ToolStripActionButton.cs">
<SubType>Component</SubType>
</Compile>
@ -236,6 +242,7 @@
<Compile Include="Dehacked\DehackedParser.cs" />
<Compile Include="Dehacked\DehackedThing.cs" />
<Compile Include="General\SHA256Hash.cs" />
<Compile Include="General\ToastManager.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@ -692,6 +699,9 @@
<EmbeddedResource Include="Controls\SidedefPartLightControl.resx">
<DependentUpon>SidedefPartLightControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\ToastControl.resx">
<DependentUpon>ToastControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\PreAndPostCommandsForm.resx">
<DependentUpon>PreAndPostCommandsForm.cs</DependentUpon>
</EmbeddedResource>

View file

@ -201,6 +201,12 @@
<Compile Include="Controls\SidedefPartLightControl.Designer.cs">
<DependentUpon>SidedefPartLightControl.cs</DependentUpon>
</Compile>
<Compile Include="Controls\ToastControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Controls\ToastControl.Designer.cs">
<DependentUpon>ToastControl.cs</DependentUpon>
</Compile>
<Compile Include="Controls\ToolStripActionButton.cs">
<SubType>Component</SubType>
</Compile>
@ -228,6 +234,7 @@
<Compile Include="Data\FlatImage.cs" />
<Compile Include="Data\ImageLoadState.cs" />
<Compile Include="General\SHA256Hash.cs" />
<Compile Include="General\ToastManager.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@ -683,6 +690,9 @@
<EmbeddedResource Include="Controls\SidedefPartLightControl.resx">
<DependentUpon>SidedefPartLightControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\ToastControl.resx">
<DependentUpon>ToastControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\PreAndPostCommandsForm.resx">
<DependentUpon>PreAndPostCommandsForm.cs</DependentUpon>
</EmbeddedResource>

View file

@ -150,7 +150,7 @@ namespace CodeImp.DoomBuilder.Config
private bool classicRendering;
private bool flatShadeVertices;
private bool alwaysShowVertices;
// These are not stored in the configuration, only used at runtime
private int defaultbrightness;
private int defaultfloorheight;
@ -420,7 +420,7 @@ namespace CodeImp.DoomBuilder.Config
classicRendering = cfg.ReadSetting("classicrendering", false);
alwaysShowVertices = cfg.ReadSetting("alwaysshowvertices", true);
flatShadeVertices = cfg.ReadSetting("flatshadevertices", false);
//mxd. Sector defaults
defaultceilheight = cfg.ReadSetting("defaultceilheight", 128);
defaultfloorheight = cfg.ReadSetting("defaultfloorheight", 0);
@ -565,6 +565,9 @@ namespace CodeImp.DoomBuilder.Config
cfg.WriteSetting("classicrendering", classicRendering);
cfg.WriteSetting("alwaysshowvertices", alwaysShowVertices);
cfg.WriteSetting("flatshadevertices", flatShadeVertices);
// Toasts
General.ToastManager.WriteSettings(cfg);
//mxd. Sector defaults
cfg.WriteSetting("defaultceilheight", defaultceilheight);

View file

@ -0,0 +1,104 @@

namespace CodeImp.DoomBuilder.Controls
{
partial class ToastControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lbText = new System.Windows.Forms.Label();
this.icon = new System.Windows.Forms.Panel();
this.lbTitle = new System.Windows.Forms.Label();
this.btnClose = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// lbText
//
this.lbText.AutoSize = true;
this.lbText.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbText.Location = new System.Drawing.Point(48, 28);
this.lbText.MaximumSize = new System.Drawing.Size(200, 0);
this.lbText.Name = "lbText";
this.lbText.Size = new System.Drawing.Size(46, 18);
this.lbText.TabIndex = 0;
this.lbText.Text = "label1";
//
// icon
//
this.icon.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
this.icon.Location = new System.Drawing.Point(10, 10);
this.icon.Name = "icon";
this.icon.Size = new System.Drawing.Size(32, 32);
this.icon.TabIndex = 1;
//
// lbTitle
//
this.lbTitle.AutoSize = true;
this.lbTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbTitle.Location = new System.Drawing.Point(48, 10);
this.lbTitle.Name = "lbTitle";
this.lbTitle.Size = new System.Drawing.Size(40, 18);
this.lbTitle.TabIndex = 2;
this.lbTitle.Text = "Title";
//
// btnClose
//
this.btnClose.BackgroundImage = global::CodeImp.DoomBuilder.Properties.Resources.Close;
this.btnClose.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.btnClose.Location = new System.Drawing.Point(385, 3);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(16, 16);
this.btnClose.TabIndex = 3;
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// ToastControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.SystemColors.Control;
this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Controls.Add(this.btnClose);
this.Controls.Add(this.lbTitle);
this.Controls.Add(this.icon);
this.Controls.Add(this.lbText);
this.DoubleBuffered = true;
this.Margin = new System.Windows.Forms.Padding(6);
this.Name = "ToastControl";
this.Size = new System.Drawing.Size(404, 49);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label lbText;
private System.Windows.Forms.Panel icon;
private System.Windows.Forms.Label lbTitle;
private System.Windows.Forms.Button btnClose;
}
}

View file

@ -0,0 +1,122 @@
#region ================== Copyright (c) 2022 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
#region ================== Namespaces
using System;
using System.Drawing;
using System.Windows.Forms;
#endregion
namespace CodeImp.DoomBuilder.Controls
{
internal partial class ToastControl : UserControl
{
#region ================== Variables
private long startime;
private long lifetime;
private bool pausedecay;
private bool remove;
#endregion
#region ================== Constructors
public ToastControl(ToastType type, string title, string text, long lifetime = 3000)
{
InitializeComponent();
this.lifetime = lifetime;
startime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
// Set icon
if(type == ToastType.INFO)
icon.BackgroundImage = SystemIcons.Information.ToBitmap();
else if(type == ToastType.WARNING)
icon.BackgroundImage = SystemIcons.Warning.ToBitmap();
else if(type == ToastType.ERROR)
icon.BackgroundImage = SystemIcons.Error.ToBitmap();
lbTitle.Text = title;
lbText.Text = text;
// The text label is auto-size, but we need to programatically set a max width so that longer texts are
// automatically broken into multiple lines
lbText.MaximumSize = new Size(Width - lbText.Location.X - Margin.Right, lbText.MaximumSize.Height);
// Resize the height of the control if the text doesn't fit vertically
if (lbText.Location.Y + lbText.Height + Margin.Bottom > Height)
Height = lbText.Location.Y + lbText.Height + lbTitle.Location.Y + Margin.Bottom;
pausedecay = false;
}
#endregion
#region ================== Methods
/// <summary>
/// Checks if the toast is decaying, i.e. the cursor is currently not inside the control.
/// </summary>
public void CheckDecay()
{
if (ClientRectangle.Contains(PointToClient(Cursor.Position)))
{
pausedecay = true;
}
else if(pausedecay)
{
pausedecay = false;
// Reset the start time, so that the control will only die "lifetime" ms after the cursor left the control
startime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
}
}
/// <summary>
/// Checks if the control is still "alive" (has not reached its lifetime).
/// </summary>
/// <returns>true if it's alive, false if it isn't</returns>
public bool IsAlive()
{
if (remove || (!pausedecay && DateTimeOffset.Now.ToUnixTimeMilliseconds() - startime > lifetime))
return false;
return true;
}
/// <summary>
/// Sets the toast to be removed.
/// </summary>
/// <param name="sender">The sender</param>
/// <param name="e">The event arguments</param>
private void btnClose_Click(object sender, EventArgs e)
{
remove = true;
}
#endregion
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -620,6 +620,20 @@ namespace CodeImp.DoomBuilder.Data
gldefsentries.Count + " dynamic light definitions, " +
glowingflats.Count + " glowing flat definitions, " + skyboxes.Count + " skybox definitions, " +
reverbs.Count + " sound environment definitions");
if (General.ErrorLogger.HasChanged)
{
string key = Actions.Action.GetShortcutKeyDesc(General.Actions.GetActionByName("builder_showerrors").ShortcutKey);
string keymessage = "";
if (!string.IsNullOrEmpty(key))
keymessage = $" ({key})";
if (General.ErrorLogger.IsWarningAdded)
General.ToastManager.ShowToast("resourcewarningsanderrors", ToastType.WARNING, ToastManager.TITLE_WARNING, $"There were warnings while loading the resources. Please review the messages in the Warnings and Errors window{keymessage}.", "There were warnings while loading the resources.");
else if(General.ErrorLogger.IsErrorAdded)
General.ToastManager.ShowToast("resourcewarningsanderrors", ToastType.ERROR, ToastManager.TITLE_ERROR, $"There were errors while loading the resources. Please review the messages in the Warnings and Errors window{keymessage}.", "There were errors while loading the resources.");
}
}
// This unloads all data

View file

@ -1061,7 +1061,15 @@ namespace CodeImp.DoomBuilder.Editing
protected virtual void ToggleHighlight()
{
General.Settings.UseHighlight = !General.Settings.UseHighlight;
General.Interface.DisplayStatus(StatusType.Action, "Highlight is now " + (General.Settings.UseHighlight ? "ON" : "OFF") + ".");
string shortmessage = "Highlight is now " + (General.Settings.UseHighlight ? "ON" : "OFF") + ".";
string message = "Highlight is now " + (General.Settings.UseHighlight ? "ON" : "OFF") + ".";
string key = Actions.Action.GetShortcutKeyDesc(General.Actions.Current.ShortcutKey);
if (!string.IsNullOrEmpty(key))
message += $" Press '{key}' to toggle.";
General.ToastManager.ShowToast("togglehighlight", ToastType.INFO, "Changed highlight", message, new StatusInfo(StatusType.Action, shortmessage));
// Redraw display to show changes
General.Interface.RedrawDisplay();

View file

@ -236,6 +236,9 @@ namespace CodeImp.DoomBuilder
//misc
private static readonly Random random = new Random(); //mxd
// Toasts
private static ToastManager toastmanager;
#endregion
#region ================== Properties
@ -280,6 +283,7 @@ namespace CodeImp.DoomBuilder
public static EditingManager Editing { get { return editing; } }
public static ErrorLogger ErrorLogger { get { return errorlogger; } }
public static string CommitHash { get { return commithash; } } //mxd
public static ToastManager ToastManager { get => toastmanager; }
#endregion
@ -661,7 +665,7 @@ namespace CodeImp.DoomBuilder
General.WriteLogLine("Temporary path: \"" + temppath + "\"");
General.WriteLogLine("Local settings path: \"" + settingspath + "\"");
General.WriteLogLine("Command-line arguments: \"" + string.Join(" ", args) + "\""); //mxd
// Load configuration
General.WriteLogLine("Loading program configuration...");
settings = new ProgramConfiguration();
@ -700,11 +704,20 @@ namespace CodeImp.DoomBuilder
mainwindow.Show();
mainwindow.Update();
}
// Create the toast manager after the main windows, but before plugins are loaded,
// since the plugins can register toasts. Also register toasts for the core
toastmanager = new ToastManager(mainwindow.Display);
RegisterToasts();
// Load plugin manager
General.WriteLogLine("Loading plugins...");
plugins = new PluginManager();
plugins.LoadAllPlugins();
// Register toasts from actions. This has to be done after all plugins are loaded
toastmanager.RegisterActions();
toastmanager.LoadSettings(settings.Config);
// Load game configurations
General.WriteLogLine("Loading game configurations...");
@ -800,6 +813,11 @@ namespace CodeImp.DoomBuilder
}
}
private static void RegisterToasts()
{
toastmanager.RegisterToast("resourcewarningsanderrors", "Resource warnings and errors", "When there are errors or warning while (re)loading the resources");
}
// This parses the command line arguments
private static void ParseCommandLineArgs(string[] args)
{

View file

@ -0,0 +1,445 @@
#region ================== Copyright (c) 2022 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Windows;
#endregion
namespace CodeImp.DoomBuilder
{
public enum ToastType
{
INFO,
WARNING,
ERROR
}
public enum ToastAnchor
{
TOPLEFT = 1,
TOPRIGHT,
BOTTOMRIGHT,
BOTTOMLEFT
}
internal class ToastRegistryEntry
{
public bool Enabled { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ToastRegistryEntry(string name, string title, string description, bool enabled)
{
Enabled = enabled;
Name = name;
Title = title;
Description = description;
}
}
public class ToastManager
{
#region ================== Static variables
public static readonly string TITLE_INFO = "Information";
public static readonly string TITLE_WARNING = "Warning";
public static readonly string TITLE_ERROR = "Error";
#endregion
#region ================== Variables
private List<ToastControl> toasts;
private Control bindcontrol;
private Timer timer;
private bool enabled;
private ToastAnchor anchor;
private long duration;
private Dictionary<string, ToastRegistryEntry> registry;
#endregion
#region ================== Properties
internal bool Enabled { get => enabled; set => enabled = value; }
internal ToastAnchor Anchor { get => anchor; set => anchor = value; }
internal long Duration { get => duration; set => duration = value; }
internal Dictionary<string, ToastRegistryEntry> Registry { get => registry; }
#endregion
#region ================== Constructors
public ToastManager(Control bindcontrol)
{
toasts = new List<ToastControl>();
this.bindcontrol = bindcontrol;
// Create the timer that will handle moving the toasts. Do not start it, though
timer = new Timer();
timer.Interval = 1; // Actually only called every 1/64 second, because Windows
timer.Tick += UpdateEvent;
// Create registry and load toasts from actions
registry = new Dictionary<string, ToastRegistryEntry>();
}
#endregion
#region ================== Events
private void UpdateEvent(object sender, EventArgs args)
{
if (toasts.Count == 0)
return;
// Go through all toasts and check if they should decay or not. Remove toasts that reached their lifetime
for (int i = toasts.Count - 1; i >= 0; i--)
{
toasts[i].CheckDecay();
if (!toasts[i].IsAlive())
{
bindcontrol.Controls.Remove(toasts[i]);
toasts[i].Dispose(); // Dispose, otherwise it'll leak
toasts.RemoveAt(i);
}
}
// No toasts left, so we should stop the timer
if (toasts.Count == 0)
{
timer.Stop();
return;
}
ToastControl ft = toasts[0];
// We only need to update the first toasts if it didn't reach it end position yet
bool needsupdate =
((anchor == ToastAnchor.TOPLEFT || anchor == ToastAnchor.TOPRIGHT) && ft.Location.Y != ft.Margin.Top)
||
((anchor == ToastAnchor.BOTTOMLEFT || anchor == ToastAnchor.BOTTOMRIGHT) && ft.Location.Y != bindcontrol.Height - ft.Height - ft.Margin.Bottom)
;
if(needsupdate)
{
int left;
int top;
if (anchor == ToastAnchor.TOPLEFT || anchor == ToastAnchor.BOTTOMLEFT)
left = ft.Margin.Right;
else
left = bindcontrol.Width - ft.Width - ft.Margin.Right;
// This moves the toast up or down a bit, depending on its anchor position. How fast this happens depends on
// the control's height, i.e. no matter the height a toast will always take the same time to slide in
// TODO: make it dependent on elapsed time
if (anchor == ToastAnchor.TOPLEFT || anchor == ToastAnchor.TOPRIGHT)
top = ft.Location.Y + ft.Height / 5;
else
top = ft.Location.Y - ft.Height / 5;
Point newLocation = new Point(left, top);
// If the movement overshot the final position snap it back to the final position
if ((anchor == ToastAnchor.BOTTOMLEFT || anchor == ToastAnchor.BOTTOMRIGHT) && newLocation.Y < bindcontrol.Height - ft.Height - ft.Margin.Bottom)
newLocation.Y = bindcontrol.Height - ft.Height - ft.Margin.Bottom;
else if ((anchor == ToastAnchor.TOPLEFT || anchor == ToastAnchor.TOPRIGHT) && newLocation.Y > ft.Margin.Top)
newLocation.Y = ft.Margin.Top;
ft.Location = newLocation;
}
if (toasts.Count > 1)
{
// Align all other toasts to their predecessor
for (int i = 1; i < toasts.Count; i++)
{
int top;
if (anchor == ToastAnchor.TOPLEFT || anchor == ToastAnchor.TOPRIGHT)
top = toasts[i - 1].Bottom + toasts[i - 1].Margin.Bottom;
else
top = toasts[i - 1].Location.Y - toasts[i].Height - toasts[i].Margin.Bottom;
toasts[i].Location = new Point(
ft.Location.X,
top
);
}
}
}
#endregion
#region ================== Methods
public void LoadSettings(Configuration cfg)
{
enabled = cfg.ReadSetting("toasts.enabled", true);
anchor = GetAnchorFromNumber(cfg.ReadSetting("toasts.anchor", 3));
duration = cfg.ReadSetting("toasts.duration", 3000);
// Make sure the duration is set to something sensible
if (duration <= 0)
duration = 3000;
IDictionary toastactionenableddict = cfg.ReadSetting("toasts.registry", new Hashtable());
foreach (string key in toastactionenableddict.Keys)
{
//string key = de.Key.ToString();
if (registry.ContainsKey(key))
registry[key].Enabled = cfg.ReadSetting($"toasts.registry.{key}", true);
}
}
/// <summary>
/// Writes the settings to a configuration with a prefix.
/// </summary>
/// <param name="cfg">The Configuration</param>
/// <param name="prefix">The prefix</param>
public void WriteSettings(Configuration cfg)
{
cfg.WriteSetting("toasts.enabled", Enabled);
cfg.WriteSetting("toasts.anchor", (int)Anchor);
cfg.WriteSetting("toasts.duration", Duration);
foreach (string key in Registry.Keys)
{
// true is the default value, so we only need to save it if it's false
if (Registry[key].Enabled == false)
cfg.WriteSetting($"toasts.registry.{key}", false);
else
cfg.DeleteSetting($"toasts.registry.{key}");
}
}
/// <summary>
/// Registers toast from all defined actions.
/// </summary>
public void RegisterActions()
{
foreach (Actions.Action action in General.Actions.GetAllActions().Where(a => a.RegisterToast))
{
if (!registry.ContainsKey(action.Name))
registry[action.Name] = new ToastRegistryEntry(action.Name, action.Title, action.Description, true);
}
}
/// <summary>
/// Registers a toast by name. Automatically prepends the assembly name.
/// </summary>
/// <param name="name">Name of the toast (without assembly)</param>
/// <param name="title">Title to show in the toast preferences dialog</param>
/// <param name="description">Description to show in the toast preferences dialog</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public void RegisterToast(string name, string title, string description)
{
string fullname = Assembly.GetCallingAssembly().GetName().Name.ToLowerInvariant() + $"_{name}";
if (registry.ContainsKey(fullname))
{
General.WriteLogLine($"Tried to register toast \"{fullname}\", but it is already registered");
return;
}
registry[fullname] = new ToastRegistryEntry(fullname, title, description, true);
}
/// <summary>
/// Gets the ToastAnchor from a number. Makes sure the input is valid, otherwise returns a default.
/// </summary>
/// <param name="number">The number</param>
/// <returns>The appropriate ToastAnchor, or BOTTOMRIGHT if input is not valid</returns>
public static ToastAnchor GetAnchorFromNumber(int number)
{
return Enum.IsDefined(typeof(ToastAnchor), number) ? (ToastAnchor)number : ToastAnchor.BOTTOMRIGHT;
}
/// <summary>
/// Shows a new toast.
/// </summary>
/// <param name="type">Toast type</param>
/// <param name="message">The message body of the toast</param>
public void ShowToast(ToastType type, string title, string message, string shortmessage = "")
{
StatusType st = type == ToastType.INFO ? StatusType.Info : StatusType.Warning;
if (!enabled)
{
General.Interface.DisplayStatus(new StatusInfo(st, shortmessage));
return;
}
if (type == ToastType.WARNING)
title = "Warning";
else if (type == ToastType.ERROR)
title = "Error";
CreateToast(type, title, message);
}
/// <summary>
/// Shows a new toast. Deducts the title from the type.
/// </summary>
/// <param name="name">Name of the toast</param>
/// <param name="type">Type of the toast</param>
/// <param name="message">Message to show</param>
/// <param name="statusinfo">StatusInfo to use when toasts are disabled</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public void ShowToast(string name, ToastType type, string message, StatusInfo statusinfo)
{
string fullname = Assembly.GetCallingAssembly().GetName().Name.ToLowerInvariant() + $"_{name}";
string title = "Information";
if (type == ToastType.WARNING)
title = "Warning";
else if (type == ToastType.ERROR)
title = "Error";
CreateToast(fullname, type, title, message, statusinfo);
}
/// <summary>
/// Shows a new toast.
/// </summary>
/// <param name="name">Name of the toast</param>
/// <param name="type">Type of the toast</param>
/// <param name="title">Title to show</param>
/// <param name="message">Message to show</param>
/// <param name="statusinfo">StatusInfo to use when toasts are disabled</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public void ShowToast(string name, ToastType type, string title, string message, StatusInfo statusinfo)
{
string fullname = Assembly.GetCallingAssembly().GetName().Name.ToLowerInvariant() + $"_{name}";
CreateToast(fullname, type, title, message, statusinfo);
}
/// <summary>
/// Shows a new toast.
/// </summary>
/// <param name="name">Name of the toast</param>
/// <param name="type">Type of the toast</param>
/// <param name="title">Title to show</param>
/// <param name="message">Message to show</param>
/// <param name="shortmessage">Message to show in the status bar if toasts are disabled. Should not include line breaks</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public void ShowToast(string name, ToastType type, string title, string message, string shortmessage = null)
{
string fullname = Assembly.GetCallingAssembly().GetName().Name.ToLowerInvariant() + $"_{name}";
StatusType st = type == ToastType.INFO ? StatusType.Info : StatusType.Warning;
if (string.IsNullOrWhiteSpace(shortmessage))
shortmessage = message;
CreateToast(fullname, type, title, message, new StatusInfo(st, shortmessage));
}
/// <summary>
/// Creates a toast.
/// </summary>
/// <param name="fullname">Full name (i.e. assembly and toast name) of the toast</param>
/// <param name="type">Type of the toast</param>
/// <param name="title">Title to show</param>
/// <param name="message">Message to show</param>
/// <param name="statusinfo">StatusInfo to use when toasts are disabled</param>
private void CreateToast(string fullname, ToastType type, string title, string message, StatusInfo statusinfo)
{
if (!enabled || registry[fullname]?.Enabled == false)
{
General.Interface.DisplayStatus(statusinfo);
return;
}
if (!registry.ContainsKey(fullname))
{
General.ErrorLogger.Add(ErrorType.Warning, $"Toast setting for \"{fullname}\" is not in the registry. Defaulting to show the toast.");
}
else if (registry[fullname].Enabled == false)
{
General.Interface.DisplayStatus(statusinfo);
return;
}
CreateToast(type, title, message);
}
/// <summary>
/// Creates a toast.
/// </summary>
/// <param name="type">Type of the toast</param>
/// <param name="title">Title to show</param>
/// <param name="message">Message to show</param>
private void CreateToast(ToastType type, string title, string message)
{
ToastControl tc = new ToastControl(type, title, message, duration);
// Set the initial y position of the control so that it's outside of the control the toast manager is bound to.
// No need to care about the x position, since that will be set in the update event anyway
if (anchor == ToastAnchor.TOPLEFT || anchor == ToastAnchor.TOPRIGHT)
tc.Location = new Point(0, -tc.Height);
else
tc.Location = new Point(0, bindcontrol.Height);
toasts.Insert(0, tc);
bindcontrol.Controls.Add(tc);
// Need to set the toast to be at the front, otherwise the new control would be behind the control the toast manager
// is bound to
bindcontrol.Controls.SetChildIndex(tc, 0);
// Start the timer so that the toast is moved into view
if (!timer.Enabled)
timer.Start();
// Play a sound for warnings and errors
if (type == ToastType.WARNING)
General.MessageBeep(MessageBeepType.Warning);
else if (type == ToastType.ERROR)
General.MessageBeep(MessageBeepType.Error);
}
#endregion
}
}

View file

@ -1177,6 +1177,7 @@ togglebrightness //mxd
allowmouse = true;
allowscroll = true;
default = 66; //B
registertoast = true;
}
togglehighlight
@ -1188,6 +1189,7 @@ togglehighlight
allowmouse = true;
allowscroll = true;
default = 72; // H
registertoast = true;
}
visualselect
@ -1327,6 +1329,7 @@ gztoggleeventlines
allowmouse = false;
allowscroll = false;
default = 73;
registertoast = true;
}
gztogglevisualvertices
@ -1359,6 +1362,7 @@ gztoggleenhancedrendering
allowmouse = true;
allowscroll = false;
default = 9; //Tab
registertoast = true;
}
//////////////////////////////

View file

@ -1382,7 +1382,16 @@ namespace CodeImp.DoomBuilder.VisualModes
public void ToggleHighlight()
{
General.Settings.UseHighlight = !General.Settings.UseHighlight;
General.Interface.DisplayStatus(StatusType.Action, "Highlight is now " + (General.Settings.UseHighlight ? "ON" : "OFF") + ".");
string shorttext = "Highlight is now " + (General.Settings.UseHighlight ? "ON" : "OFF") + ".";
string text = shorttext;
string key = Actions.Action.GetShortcutKeyDesc(General.Actions.Current.ShortcutKey);
if (!string.IsNullOrEmpty(key))
text += $" Press '{key}' to toggle.";
General.ToastManager.ShowToast("togglehighlight", ToastType.INFO, "Changed highlight", text, shorttext);
}
#endregion

View file

@ -183,7 +183,7 @@ namespace CodeImp.DoomBuilder.Windows
public static Size ScaledIconSize = new Size(16, 16); //mxd
public static SizeF DPIScaler = new SizeF(1.0f, 1.0f); //mxd
public int ProcessingCount { get { return processingcount; } }
#endregion
#region ================== Constructor / Disposer
@ -750,9 +750,9 @@ namespace CodeImp.DoomBuilder.Windows
}
#endregion
#region ================== Statusbar
// This updates the status bar
private void UpdateStatusbar()
{
@ -3042,7 +3042,15 @@ namespace CodeImp.DoomBuilder.Windows
Renderer.FullBrightness = !Renderer.FullBrightness;
buttonfullbrightness.Checked = Renderer.FullBrightness;
itemfullbrightness.Checked = Renderer.FullBrightness;
General.Interface.DisplayStatus(StatusType.Action, "Full Brightness is now " + (Renderer.FullBrightness ? "ON" : "OFF"));
string shorttext = "Full brightness is now " + (Renderer.FullBrightness ? "ON" : "OFF") + ".";
string text = shorttext;
string key = Actions.Action.GetShortcutKeyDesc(General.Actions.Current.ShortcutKey);
if (!string.IsNullOrEmpty(key))
text += $" Press '{key}' to toggle.";
General.ToastManager.ShowToast("togglebrightness", ToastType.INFO, "Changed full brightness", text, shorttext);
// Redraw display to show changes
General.Interface.RedrawDisplay();
@ -3252,9 +3260,17 @@ namespace CodeImp.DoomBuilder.Windows
General.Settings.GZDrawLightsMode = (General.Settings.EnhancedRenderingEffects ? LightRenderMode.ALL : LightRenderMode.NONE);
General.Settings.GZDrawModelsMode = (General.Settings.EnhancedRenderingEffects ? ModelRenderMode.ALL : ModelRenderMode.NONE);
string shorttext = "Enhanced rendering effects are " + (General.Settings.EnhancedRenderingEffects ? "ENABLED" : "DISABLED") + ".";
string text = shorttext;
string key = Actions.Action.GetShortcutKeyDesc(General.Actions.Current.ShortcutKey);
if (!string.IsNullOrEmpty(key))
text += $" Press '{key}' to toggle.";
General.ToastManager.ShowToast("gztoggleenhancedrendering", ToastType.INFO, "Changed enhanced rendering", text, shorttext);
UpdateGZDoomPanel();
UpdateViewMenu();
DisplayStatus(StatusType.Info, "Enhanced rendering effects are " + (General.Settings.EnhancedRenderingEffects ? "ENABLED" : "DISABLED"));
}
//mxd
@ -3307,7 +3323,15 @@ namespace CodeImp.DoomBuilder.Windows
itemtoggleeventlines.Checked = General.Settings.GZShowEventLines;
buttontoggleeventlines.Checked = General.Settings.GZShowEventLines;
General.MainWindow.DisplayStatus(StatusType.Action, "Event lines are " + (General.Settings.GZShowEventLines ? "ENABLED" : "DISABLED"));
string shorttext = "Event lines are now " + (General.Settings.GZShowEventLines ? "ENABLED" : "DISABLED") + ".";
string text = shorttext;
string key = Actions.Action.GetShortcutKeyDesc(General.Actions.Current.ShortcutKey);
if (!string.IsNullOrEmpty(key))
text += $" Press '{key}' to toggle.";
General.ToastManager.ShowToast("gztoggleeventlines", ToastType.INFO, "Changed event line visibility", text, shorttext);
General.MainWindow.RedrawDisplay();
General.MainWindow.UpdateGZDoomPanel();
}

View file

@ -38,6 +38,7 @@ namespace CodeImp.DoomBuilder.Windows
System.Windows.Forms.Label label18;
System.Windows.Forms.Label label1;
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PreferencesForm));
this.autolaunchontest = new System.Windows.Forms.CheckBox();
this.texturesizesbelow = new System.Windows.Forms.CheckBox();
this.blackbrowsers = new System.Windows.Forms.CheckBox();
this.checkforupdates = new System.Windows.Forms.CheckBox();
@ -212,7 +213,20 @@ namespace CodeImp.DoomBuilder.Windows
this.tabpasting = new System.Windows.Forms.TabPage();
this.label16 = new System.Windows.Forms.Label();
this.pasteoptions = new CodeImp.DoomBuilder.Controls.PasteOptionsControl();
this.autolaunchontest = new System.Windows.Forms.CheckBox();
this.tabtoasts = new System.Windows.Forms.TabPage();
this.groupBox10 = new System.Windows.Forms.GroupBox();
this.lvToastActions = new System.Windows.Forms.ListView();
this.title = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.description = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.tbToastDuration = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
this.label12 = new System.Windows.Forms.Label();
this.cbToastsEnabled = new System.Windows.Forms.CheckBox();
this.gbToastPosition = new System.Windows.Forms.GroupBox();
this.label6 = new System.Windows.Forms.Label();
this.rbToastPosBR = new System.Windows.Forms.RadioButton();
this.rbToastPosBL = new System.Windows.Forms.RadioButton();
this.rbToastPosTR = new System.Windows.Forms.RadioButton();
this.rbToastPosTL = new System.Windows.Forms.RadioButton();
groupBox1 = new System.Windows.Forms.GroupBox();
label7 = new System.Windows.Forms.Label();
label5 = new System.Windows.Forms.Label();
@ -257,6 +271,9 @@ namespace CodeImp.DoomBuilder.Windows
this.groupBox6.SuspendLayout();
this.previewgroup.SuspendLayout();
this.tabpasting.SuspendLayout();
this.tabtoasts.SuspendLayout();
this.groupBox10.SuspendLayout();
this.gbToastPosition.SuspendLayout();
this.SuspendLayout();
//
// groupBox1
@ -291,6 +308,16 @@ namespace CodeImp.DoomBuilder.Windows
groupBox1.TabStop = false;
groupBox1.Text = " Options ";
//
// autolaunchontest
//
this.autolaunchontest.AutoSize = true;
this.autolaunchontest.Location = new System.Drawing.Point(16, 392);
this.autolaunchontest.Name = "autolaunchontest";
this.autolaunchontest.Size = new System.Drawing.Size(301, 17);
this.autolaunchontest.TabIndex = 50;
this.autolaunchontest.Text = "Automatically launch engine when test parameters change";
this.autolaunchontest.UseVisualStyleBackColor = true;
//
// texturesizesbelow
//
this.texturesizesbelow.AutoSize = true;
@ -400,13 +427,13 @@ namespace CodeImp.DoomBuilder.Windows
this.vertexScale.BackColor = System.Drawing.Color.Transparent;
this.vertexScale.LargeChange = 1;
this.vertexScale.Location = new System.Drawing.Point(127, 119);
this.vertexScale.Minimum = 1;
this.vertexScale.Maximum = 40;
this.vertexScale.Minimum = 1;
this.vertexScale.Name = "vertexScale";
this.vertexScale.Size = new System.Drawing.Size(116, 45);
this.vertexScale.TabIndex = 4;
this.vertexScale.TickStyle = System.Windows.Forms.TickStyle.TopLeft;
this.vertexScale.TickFrequency = 4;
this.vertexScale.TickStyle = System.Windows.Forms.TickStyle.TopLeft;
this.vertexScale.Value = 1;
this.vertexScale.ValueChanged += new System.EventHandler(this.vertexScale_ValueChanged);
//
@ -644,10 +671,11 @@ namespace CodeImp.DoomBuilder.Windows
this.cbFlatShadeVertices.AutoSize = true;
this.cbFlatShadeVertices.Location = new System.Drawing.Point(18, 489);
this.cbFlatShadeVertices.Name = "cbFlatShadeVertices";
this.cbFlatShadeVertices.Size = new System.Drawing.Size(175, 17);
this.cbFlatShadeVertices.Size = new System.Drawing.Size(115, 17);
this.cbFlatShadeVertices.TabIndex = 1;
this.cbFlatShadeVertices.Text = "Flat shade vertices";
this.toolTip1.SetToolTip(this.cbFlatShadeVertices, "When enabled, vertices in classic mode will be drawn with flat shading instead of a raised border.");
this.toolTip1.SetToolTip(this.cbFlatShadeVertices, "When enabled, vertices in classic mode will be drawn with flat shading instead of" +
" a raised border.");
this.cbFlatShadeVertices.UseVisualStyleBackColor = true;
//
// label32
@ -756,6 +784,7 @@ namespace CodeImp.DoomBuilder.Windows
this.tabs.Controls.Add(this.tabcolors);
this.tabs.Controls.Add(this.tabscripteditor);
this.tabs.Controls.Add(this.tabpasting);
this.tabs.Controls.Add(this.tabtoasts);
this.tabs.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tabs.Location = new System.Drawing.Point(11, 13);
this.tabs.Name = "tabs";
@ -777,7 +806,7 @@ namespace CodeImp.DoomBuilder.Windows
this.tabinterface.Location = new System.Drawing.Point(4, 22);
this.tabinterface.Name = "tabinterface";
this.tabinterface.Padding = new System.Windows.Forms.Padding(5);
this.tabinterface.Size = new System.Drawing.Size(680, 501);
this.tabinterface.Size = new System.Drawing.Size(680, 526);
this.tabinterface.TabIndex = 0;
this.tabinterface.Text = "Interface";
this.tabinterface.UseVisualStyleBackColor = true;
@ -1239,7 +1268,7 @@ namespace CodeImp.DoomBuilder.Windows
this.tabkeys.Location = new System.Drawing.Point(4, 22);
this.tabkeys.Name = "tabkeys";
this.tabkeys.Padding = new System.Windows.Forms.Padding(3);
this.tabkeys.Size = new System.Drawing.Size(680, 501);
this.tabkeys.Size = new System.Drawing.Size(680, 526);
this.tabkeys.TabIndex = 1;
this.tabkeys.Text = "Controls";
this.tabkeys.UseVisualStyleBackColor = true;
@ -1429,7 +1458,7 @@ namespace CodeImp.DoomBuilder.Windows
this.tabcolors.Location = new System.Drawing.Point(4, 22);
this.tabcolors.Name = "tabcolors";
this.tabcolors.Padding = new System.Windows.Forms.Padding(5);
this.tabcolors.Size = new System.Drawing.Size(680, 501);
this.tabcolors.Size = new System.Drawing.Size(680, 526);
this.tabcolors.TabIndex = 2;
this.tabcolors.Text = "Appearance";
this.tabcolors.UseVisualStyleBackColor = true;
@ -1872,7 +1901,7 @@ namespace CodeImp.DoomBuilder.Windows
this.tabscripteditor.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tabscripteditor.Location = new System.Drawing.Point(4, 22);
this.tabscripteditor.Name = "tabscripteditor";
this.tabscripteditor.Size = new System.Drawing.Size(680, 501);
this.tabscripteditor.Size = new System.Drawing.Size(680, 526);
this.tabscripteditor.TabIndex = 4;
this.tabscripteditor.Text = "Script Editor";
this.tabscripteditor.UseVisualStyleBackColor = true;
@ -2365,7 +2394,7 @@ namespace CodeImp.DoomBuilder.Windows
this.tabpasting.Location = new System.Drawing.Point(4, 22);
this.tabpasting.Name = "tabpasting";
this.tabpasting.Padding = new System.Windows.Forms.Padding(5);
this.tabpasting.Size = new System.Drawing.Size(680, 501);
this.tabpasting.Size = new System.Drawing.Size(680, 526);
this.tabpasting.TabIndex = 3;
this.tabpasting.Text = "Pasting ";
this.tabpasting.UseVisualStyleBackColor = true;
@ -2392,15 +2421,161 @@ namespace CodeImp.DoomBuilder.Windows
this.pasteoptions.Size = new System.Drawing.Size(666, 427);
this.pasteoptions.TabIndex = 0;
//
// autolaunchontest
// tabtoasts
//
this.autolaunchontest.AutoSize = true;
this.autolaunchontest.Location = new System.Drawing.Point(16, 392);
this.autolaunchontest.Name = "autolaunchontest";
this.autolaunchontest.Size = new System.Drawing.Size(301, 17);
this.autolaunchontest.TabIndex = 50;
this.autolaunchontest.Text = "Automatically launch engine when test parameters change";
this.autolaunchontest.UseVisualStyleBackColor = true;
this.tabtoasts.Controls.Add(this.groupBox10);
this.tabtoasts.Controls.Add(this.tbToastDuration);
this.tabtoasts.Controls.Add(this.label12);
this.tabtoasts.Controls.Add(this.cbToastsEnabled);
this.tabtoasts.Controls.Add(this.gbToastPosition);
this.tabtoasts.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tabtoasts.Location = new System.Drawing.Point(4, 22);
this.tabtoasts.Name = "tabtoasts";
this.tabtoasts.Size = new System.Drawing.Size(680, 526);
this.tabtoasts.TabIndex = 5;
this.tabtoasts.Text = "Toasts";
this.tabtoasts.UseVisualStyleBackColor = true;
//
// groupBox10
//
this.groupBox10.Controls.Add(this.lvToastActions);
this.groupBox10.Location = new System.Drawing.Point(11, 164);
this.groupBox10.Name = "groupBox10";
this.groupBox10.Size = new System.Drawing.Size(653, 346);
this.groupBox10.TabIndex = 5;
this.groupBox10.TabStop = false;
this.groupBox10.Text = "Toasts";
//
// lvToastActions
//
this.lvToastActions.CheckBoxes = true;
this.lvToastActions.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.title,
this.description});
this.lvToastActions.FullRowSelect = true;
this.lvToastActions.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.lvToastActions.HideSelection = false;
this.lvToastActions.Location = new System.Drawing.Point(6, 19);
this.lvToastActions.MultiSelect = false;
this.lvToastActions.Name = "lvToastActions";
this.lvToastActions.ShowGroups = false;
this.lvToastActions.Size = new System.Drawing.Size(641, 321);
this.lvToastActions.TabIndex = 5;
this.lvToastActions.UseCompatibleStateImageBehavior = false;
this.lvToastActions.View = System.Windows.Forms.View.Details;
//
// title
//
this.title.Text = "Title";
//
// description
//
this.description.Text = "Description";
//
// tbToastDuration
//
this.tbToastDuration.AllowDecimal = false;
this.tbToastDuration.AllowExpressions = false;
this.tbToastDuration.AllowNegative = false;
this.tbToastDuration.AllowRelative = false;
this.tbToastDuration.ButtonStep = 1;
this.tbToastDuration.ButtonStepBig = 10F;
this.tbToastDuration.ButtonStepFloat = 1F;
this.tbToastDuration.ButtonStepSmall = 0.1F;
this.tbToastDuration.ButtonStepsUseModifierKeys = false;
this.tbToastDuration.ButtonStepsWrapAround = false;
this.tbToastDuration.Location = new System.Drawing.Point(350, 45);
this.tbToastDuration.Name = "tbToastDuration";
this.tbToastDuration.Size = new System.Drawing.Size(70, 24);
this.tbToastDuration.StepValues = null;
this.tbToastDuration.TabIndex = 3;
this.tbToastDuration.WhenTextChanged += new System.EventHandler(this.tbToastDuration_WhenTextChanged);
//
// label12
//
this.label12.AutoSize = true;
this.label12.Location = new System.Drawing.Point(217, 50);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(99, 13);
this.label12.TabIndex = 2;
this.label12.Text = "Duration (seconds):";
//
// cbToastsEnabled
//
this.cbToastsEnabled.AutoSize = true;
this.cbToastsEnabled.Location = new System.Drawing.Point(11, 15);
this.cbToastsEnabled.Name = "cbToastsEnabled";
this.cbToastsEnabled.Size = new System.Drawing.Size(90, 17);
this.cbToastsEnabled.TabIndex = 1;
this.cbToastsEnabled.Text = "Enable toasts";
this.cbToastsEnabled.UseVisualStyleBackColor = true;
this.cbToastsEnabled.CheckedChanged += new System.EventHandler(this.cbToastsEnabled_CheckedChanged);
//
// gbToastPosition
//
this.gbToastPosition.Controls.Add(this.label6);
this.gbToastPosition.Controls.Add(this.rbToastPosBR);
this.gbToastPosition.Controls.Add(this.rbToastPosBL);
this.gbToastPosition.Controls.Add(this.rbToastPosTR);
this.gbToastPosition.Controls.Add(this.rbToastPosTL);
this.gbToastPosition.Location = new System.Drawing.Point(11, 38);
this.gbToastPosition.Name = "gbToastPosition";
this.gbToastPosition.Size = new System.Drawing.Size(200, 120);
this.gbToastPosition.TabIndex = 0;
this.gbToastPosition.TabStop = false;
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(62, 56);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(73, 13);
this.label6.TabIndex = 4;
this.label6.Text = "Toast position";
//
// rbToastPosBR
//
this.rbToastPosBR.AutoSize = true;
this.rbToastPosBR.Location = new System.Drawing.Point(180, 101);
this.rbToastPosBR.Name = "rbToastPosBR";
this.rbToastPosBR.Size = new System.Drawing.Size(14, 13);
this.rbToastPosBR.TabIndex = 3;
this.rbToastPosBR.TabStop = true;
this.rbToastPosBR.Tag = "3";
this.rbToastPosBR.UseVisualStyleBackColor = true;
//
// rbToastPosBL
//
this.rbToastPosBL.AutoSize = true;
this.rbToastPosBL.Location = new System.Drawing.Point(6, 101);
this.rbToastPosBL.Name = "rbToastPosBL";
this.rbToastPosBL.Size = new System.Drawing.Size(14, 13);
this.rbToastPosBL.TabIndex = 2;
this.rbToastPosBL.TabStop = true;
this.rbToastPosBL.Tag = "4";
this.rbToastPosBL.UseVisualStyleBackColor = true;
//
// rbToastPosTR
//
this.rbToastPosTR.AutoSize = true;
this.rbToastPosTR.Location = new System.Drawing.Point(180, 12);
this.rbToastPosTR.Name = "rbToastPosTR";
this.rbToastPosTR.Size = new System.Drawing.Size(14, 13);
this.rbToastPosTR.TabIndex = 1;
this.rbToastPosTR.TabStop = true;
this.rbToastPosTR.Tag = "2";
this.rbToastPosTR.UseVisualStyleBackColor = true;
//
// rbToastPosTL
//
this.rbToastPosTL.AutoSize = true;
this.rbToastPosTL.Location = new System.Drawing.Point(6, 12);
this.rbToastPosTL.Name = "rbToastPosTL";
this.rbToastPosTL.Size = new System.Drawing.Size(14, 13);
this.rbToastPosTL.TabIndex = 0;
this.rbToastPosTL.TabStop = true;
this.rbToastPosTL.Tag = "1";
this.rbToastPosTL.UseVisualStyleBackColor = true;
//
// PreferencesForm
//
@ -2473,6 +2648,11 @@ namespace CodeImp.DoomBuilder.Windows
this.groupBox6.PerformLayout();
this.previewgroup.ResumeLayout(false);
this.tabpasting.ResumeLayout(false);
this.tabtoasts.ResumeLayout(false);
this.tabtoasts.PerformLayout();
this.groupBox10.ResumeLayout(false);
this.gbToastPosition.ResumeLayout(false);
this.gbToastPosition.PerformLayout();
this.ResumeLayout(false);
}
@ -2654,5 +2834,19 @@ namespace CodeImp.DoomBuilder.Windows
private System.Windows.Forms.CheckBox texturesizesbelow;
private System.Windows.Forms.CheckBox cbShowFPS;
private System.Windows.Forms.CheckBox autolaunchontest;
private System.Windows.Forms.TabPage tabtoasts;
private Controls.ButtonsNumericTextbox tbToastDuration;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.CheckBox cbToastsEnabled;
private System.Windows.Forms.GroupBox gbToastPosition;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.RadioButton rbToastPosBR;
private System.Windows.Forms.RadioButton rbToastPosBL;
private System.Windows.Forms.RadioButton rbToastPosTR;
private System.Windows.Forms.RadioButton rbToastPosTL;
private System.Windows.Forms.GroupBox groupBox10;
private System.Windows.Forms.ListView lvToastActions;
private System.Windows.Forms.ColumnHeader title;
private System.Windows.Forms.ColumnHeader description;
}
}

View file

@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Actions;
using System.Globalization;
@ -268,6 +269,28 @@ namespace CodeImp.DoomBuilder.Windows
// Paste options
pasteoptions.Setup(General.Settings.PasteOptions.Copy());
// Toasts
cbToastsEnabled.Checked = General.ToastManager.Enabled;
tbToastDuration.Text = (General.ToastManager.Duration / 1000).ToString();
RadioButton rb = gbToastPosition.Controls.OfType<RadioButton>().FirstOrDefault(r => (string)r.Tag == ((int)General.ToastManager.Anchor).ToString());
if (rb != null)
rb.Checked = true;
SetToastSettingEnabled(cbToastsEnabled.Checked);
// Add checkboxes for all registered toasts
foreach(ToastRegistryEntry tre in General.ToastManager.Registry.Values.OrderBy(e => e.Title))
{
ListViewItem lvi = lvToastActions.Items.Add(tre.Title);
lvi.SubItems.Add(tre.Description);
lvi.Checked = tre.Enabled;
lvi.Tag = tre;
}
// Make the columns fit the contents
title.Width = -1;
description.Width = -1;
// Allow plugins to add tabs
this.SuspendLayout();
controller = new PreferencesController(this) { AllowAddTab = true };
@ -424,9 +447,21 @@ namespace CodeImp.DoomBuilder.Windows
// Paste options
General.Settings.PasteOptions = pasteoptions.GetOptions();
// Toasts
General.ToastManager.Enabled = cbToastsEnabled.Checked;
General.ToastManager.Anchor = (ToastAnchor)int.Parse((string)gbToastPosition.Controls.OfType<RadioButton>().FirstOrDefault(rb => rb.Checked).Tag);
General.ToastManager.Duration = tbToastDuration.GetResult(1) * 1000;
foreach(ListViewItem lvi in lvToastActions.Items)
{
if(lvi.Tag is ToastRegistryEntry tre)
General.ToastManager.Registry[tre.Name].Enabled = lvi.Checked;
}
// Let the plugins know we're closing
General.Plugins.OnClosePreferences(controller);
// Close
this.DialogResult = DialogResult.OK;
@ -1260,6 +1295,39 @@ namespace CodeImp.DoomBuilder.Windows
#endregion
#region ================== Toasts
private void tbToastDuration_WhenTextChanged(object sender, EventArgs e)
{
if (!allowapplycontrol)
return;
if (tbToastDuration.GetResult(1) <= 0)
{
allowapplycontrol = false;
tbToastDuration.Text = "1";
allowapplycontrol = true;
}
}
private void cbToastsEnabled_CheckedChanged(object sender, EventArgs e)
{
SetToastSettingEnabled(cbToastsEnabled.Checked);
}
private void SetToastSettingEnabled(bool enabled)
{
foreach (Control c in tabtoasts.Controls)
{
if (c == cbToastsEnabled)
continue;
c.Enabled = cbToastsEnabled.Checked;
}
}
#endregion
#region ================== Screenshots Stuff (mxd)
private void resetscreenshotsdir_Click(object sender, EventArgs e)
@ -1286,7 +1354,7 @@ namespace CodeImp.DoomBuilder.Windows
}
}
/*
/*
// This writes all action help files using a template and some basic info from the actions.
// Also writes actioncontents.txt with all files to be inserted into Contents.hhc.
// Only used during development. Actual button to call this has been removed.
@ -1318,5 +1386,5 @@ namespace CodeImp.DoomBuilder.Windows
File.WriteAllText(filename, contents.ToString());
}
*/
}
}
}

View file

@ -141,9 +141,6 @@
<metadata name="label1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="scriptallmanstyle.ToolTip" xml:space="preserve">
<value>When enabled, the opening brace
will be placed on a new line when

View file

@ -33,7 +33,7 @@ namespace CodeImp.DoomBuilder.Windows
public readonly string selectioninfo; //mxd
internal bool displayed;
internal StatusInfo(StatusType type, string message)
public StatusInfo(StatusType type, string message)
{
this.type = type;

View file

@ -181,6 +181,7 @@ namespace CodeImp.DoomBuilder.ThreeDFloorMode
// TMP
drawlines = new List<Line3D>();
drawpoints = new List<Vector3D>();
}
// This is called when the plugin is terminated

View file

@ -1932,7 +1932,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
else //mxd
{
General.Interface.DisplayStatus(StatusType.Warning, "This action requires a highlight or selection!");
General.ToastManager.ShowToast("makedoor", ToastType.WARNING, "Couldn't create door", "You need to highlight or select at least one sector to create a door.");
}
}

View file

@ -528,6 +528,7 @@ makedoor
allowkeys = true;
allowmouse = true;
allowscroll = true;
registertoast = true;
}
lowerfloor8
@ -1091,6 +1092,7 @@ togglegravity
allowkeys = true;
allowmouse = true;
allowscroll = true;
registertoast = true;
}
resettexture

View file

@ -3640,8 +3640,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void ToggleGravity()
{
BuilderPlug.Me.UseGravity = !BuilderPlug.Me.UseGravity;
string onoff = BuilderPlug.Me.UseGravity ? "ON" : "OFF";
General.Interface.DisplayStatus(StatusType.Action, "Gravity is now " + onoff + ".");
string shortmessage = "Gravity is now " + (BuilderPlug.Me.UseGravity ? "ON" : "OFF") + ".";
string message = shortmessage;
string key = Actions.Action.GetShortcutKeyDesc(General.Actions.Current.ShortcutKey);
if (!string.IsNullOrEmpty(key))
message += $" Press '{key}' to toggle.";
General.ToastManager.ShowToast("togglegravity", ToastType.INFO, "Changed gravity", message, new StatusInfo(StatusType.Action, shortmessage));
}
[BeginAction("resettexture")]

View file

@ -34,6 +34,11 @@ namespace CodeImp.DoomBuilder.StairSectorBuilderMode
// by the core.
//
internal class ToastMessages
{
public static readonly string ENGAGEFAILED = "engagefailed";
}
public class BuilderPlug : Plug
{
public struct Prefab
@ -119,6 +124,9 @@ namespace CodeImp.DoomBuilder.StairSectorBuilderMode
// Keep a static reference
me = this;
// Register toasts
General.ToastManager.RegisterToast(ToastMessages.ENGAGEFAILED, "Stair Sector Builder Mode starting failed", "When no linedefs or sectors are selected when entering Stair Sector Builder Mode");
}
// This is called when the plugin is terminated

View file

@ -1237,7 +1237,7 @@ namespace CodeImp.DoomBuilder.StairSectorBuilderMode
// If no lines are selected nothing can be done, so exit this mode immediately
if(General.Map.Map.SelectedLinedefsCount == 0 && General.Map.Map.SelectedSectorsCount == 0)
{
General.Interface.DisplayStatus(StatusType.Warning, "No lines or sectors selected.");
General.ToastManager.ShowToast(ToastMessages.ENGAGEFAILED, ToastType.ERROR, "Failed to start Stair Sector Builder Mode", "You need to select at least one linedef or sector to enter Stair Sector Builder Mode");
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
return;
}