-
Notifications
You must be signed in to change notification settings - Fork 52
/
KeyTipControl.cs
296 lines (259 loc) · 10.7 KB
/
KeyTipControl.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#region BSD License
/*
*
* Original BSD 3-Clause License (https://github.com/ComponentFactory/Krypton/blob/master/LICENSE)
* © Component Factory Pty Ltd, 2006 - 2016, (Version 4.5.0.0) All rights reserved.
*
* New BSD 3-Clause License (https://github.com/Krypton-Suite/Standard-Toolkit/blob/master/LICENSE)
* Modifications by Peter Wagner(aka Wagnerp) & Simon Coghlan(aka Smurf-IV), et al. 2017 - 2022. All rights reserved.
*
*/
#endregion
namespace Krypton.Ribbon
{
internal class KeyTipControl : KryptonForm
{
#region Instance Fields
private readonly KryptonRibbon _ribbon;
private List<ViewDrawRibbonKeyTip> _viewList;
private string _prefix;
private readonly bool _showDisabled;
private System.Windows.Forms.Timer _redrawTimer = null;
#endregion
#region Identity
/// <summary>
/// Initialize a new instance of the KeyTipControl class.
/// </summary>
/// <param name="ribbon">Reference to owning control instance.</param>
/// <param name="keyTips">List of key tips.</param>
/// <param name="showDisabled">True to show disabled entries, otherwise enabled.</param>
public KeyTipControl(KryptonRibbon ribbon,
KeyTipInfoList keyTips,
bool showDisabled)
{
_ribbon = ribbon;
_showDisabled = showDisabled;
// Update form properties so we do not have a border and do not show
// in the task bar. We draw the background in Magenta and set that as
// the transparency key so it is a show through window.
StartPosition = FormStartPosition.Manual;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
TransparencyKey = Color.Magenta;
#pragma warning disable CS0618 // Type or member is obsolete
UseDropShadow = false;
#pragma warning restore CS0618 // Type or member is obsolete
// Disabled key tips are show semi-transparent
if (_showDisabled)
{
Opacity = 0.5f;
}
// Define the initial set of key tips
SetKeyTips(keyTips);
}
#endregion
#region Public
/// <summary>
/// Define the set of key tips to display.
/// </summary>
/// <param name="keyTips">List of key tips.</param>
public void SetKeyTips(KeyTipInfoList keyTips)
{
// Create a new list of key tip views
_viewList = new List<ViewDrawRibbonKeyTip>();
Rectangle enclosingRect = Rectangle.Empty;
// Create a view per key tip definition
foreach (KeyTipInfo keyTip in keyTips)
{
// Create the initial rect as enclosing just the single point
if (enclosingRect.IsEmpty)
{
enclosingRect = new Rectangle(keyTip.ScreenPt, new Size(1, 1));
}
else
{
// Enlarge the rect to enclose the new point
if (keyTip.ScreenPt.X < enclosingRect.Left)
{
var diff = enclosingRect.Left - keyTip.ScreenPt.X;
enclosingRect.Width += diff;
enclosingRect.X -= diff;
}
if (keyTip.ScreenPt.X > enclosingRect.Right)
{
enclosingRect.Width += keyTip.ScreenPt.X - enclosingRect.Right;
}
if (keyTip.ScreenPt.Y < enclosingRect.Top)
{
var diff = enclosingRect.Top - keyTip.ScreenPt.Y;
enclosingRect.Height += diff;
enclosingRect.Y -= diff;
}
if (keyTip.ScreenPt.Y > enclosingRect.Bottom)
{
enclosingRect.Height += keyTip.ScreenPt.Y - enclosingRect.Bottom;
}
}
_viewList.Add(new ViewDrawRibbonKeyTip(keyTip,
_ribbon.StateCommon.RibbonKeyTip.Back,
_ribbon.StateCommon.RibbonKeyTip.Border,
_ribbon.StateCommon.RibbonKeyTip.Content));
}
// Inflate the enclosing rect to account for maximum expected key tip
enclosingRect.Inflate(50, 50);
// Remove any prefix characters
_prefix = string.Empty;
// Our position covers the enclosing rect
SetBounds(enclosingRect.X,
enclosingRect.Y,
enclosingRect.Width,
enclosingRect.Height);
StartTimer();
}
/// <summary>
/// Process the incoming key as being pressed.
/// </summary>
/// <param name="key">Key data.</param>
public void AppendKeyPress(char key)
{
// We only use uppercase characters
key = char.ToUpper(key);
// Find the new prefix with the additional key
var newPrefix = _prefix + key;
// Search for any keytip that is an exact match
foreach (ViewDrawRibbonKeyTip viewKeyTip in _viewList)
{
if (viewKeyTip.KeyTipInfo.KeyString.Equals(newPrefix))
{
// Invoke the target
viewKeyTip.KeyTipInfo.KeyTipSelect(_ribbon);
return;
}
}
// Search to see if any keytip has this as a prefix
var found = false;
foreach (ViewDrawRibbonKeyTip viewKeyTip in _viewList)
{
if (viewKeyTip.KeyTipInfo.KeyString.StartsWith(newPrefix))
{
found = true;
break;
}
}
if (found)
{
// Append the character to the prefix string
_prefix += key;
// Hide ourself and then show again to force redraw
PI.ShowWindow(Handle, PI.ShowWindowCommands.SW_HIDE);
// Use timer to force redraw
StartTimer();
}
else
{
// Escape key is not considered an error character
if (key != 27)
{
// Issue a windows error sound
PI.MessageBeep(PI.BeepType.Exclamation);
}
}
}
#endregion
#region Protected
/// <summary>
/// Gets the creation parameters.
/// </summary>
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Parent = IntPtr.Zero;
cp.Style |= unchecked((int)PI.WS_.POPUP);
cp.ExStyle |= unchecked((int)(PI.WS_EX_.TOPMOST | PI.WS_EX_.TOOLWINDOW));
return cp;
}
}
/// <summary>
/// Raises the PaintBackground event.
/// </summary>
/// <param name="pevent">An PaintEventArgs containing the event data.</param>
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// Magenta is the transparent color
pevent.Graphics.FillRectangle(Brushes.Magenta, pevent.ClipRectangle);
}
/// <summary>
/// Raises the Paint event.
/// </summary>
/// <param name="e">An PaintEventArgs containing the event data.</param>
protected override void OnPaint(PaintEventArgs e)
{
using (ViewLayoutContext layoutContext = new(this, _ribbon.Renderer))
{
foreach (ViewDrawRibbonKeyTip viewKeyTip in _viewList)
{
// Only interested in correct enabled state items
if ((_showDisabled && !viewKeyTip.KeyTipInfo.Enabled) ||
(!_showDisabled && viewKeyTip.KeyTipInfo.Enabled))
{
var visible = viewKeyTip.KeyTipInfo.Visible;
// Only make the view visible if the key tip matches the prefix
if (visible && !string.IsNullOrEmpty(_prefix))
{
visible = viewKeyTip.KeyTipInfo.KeyString.StartsWith(_prefix);
}
// Update with latest enabled/visible state
viewKeyTip.Visible = visible;
viewKeyTip.Enabled = viewKeyTip.KeyTipInfo.Enabled;
// Find the requested size
Size viewSize = viewKeyTip.GetPreferredSize(layoutContext);
// Convert the requested screen point of key tip to client
Point clientPt = PointToClient(viewKeyTip.KeyTipInfo.ScreenPt);
// Center the child at the requested screen position
clientPt.X -= viewSize.Width / 2;
clientPt.Y -= viewSize.Height / 2;
// Position the child at the calculated position
layoutContext.DisplayRectangle = new Rectangle(clientPt, viewSize);
viewKeyTip.Layout(layoutContext);
}
}
}
using (RenderContext renderContext = new(this, e.Graphics, e.ClipRectangle, _ribbon.Renderer))
{
foreach (ViewDrawRibbonKeyTip viewKeyTip in _viewList)
{
if (viewKeyTip.Visible)
{
viewKeyTip.Render(renderContext);
}
}
}
}
#endregion
#region Implementation
private void StartTimer()
{
// Start timer to take care of re drawing the display
_redrawTimer = new System.Windows.Forms.Timer
{
Interval = 1
};
_redrawTimer.Tick += OnRedrawTick;
_redrawTimer.Start();
}
private void OnRedrawTick(object sender, EventArgs e)
{
_redrawTimer = (System.Windows.Forms.Timer)sender;
_redrawTimer.Stop();
_redrawTimer.Dispose();
// Show the window and so cause it to be redrawn
if (!IsDisposed && (Handle != IntPtr.Zero))
{
PI.ShowWindow(Handle, PI.ShowWindowCommands.SW_SHOWNOACTIVATE);
}
}
#endregion
}
}