/// <summary>Creates a new instance of Mode.</summary> /// <param name="graphics">The graphics device manager to use.</param> /// <param name="content">The content manager to use.</param> /// <param name="batch">The sprite batch to use.</param> /// <param name="bEffect">The basic effect to use.</param> public Mode(GraphicsDeviceManager graphics, ContentManager content, SpriteBatch batch, BasicEffect bEffect) { Graphics = graphics; Content = content; Batch = batch; BEffect = bEffect; SwitchTo = null; Inp = new InpState(); }
/// <summary>Performs a key event on this Control.</summary> /// <param name="keys">The pressed keys.</param> /// <param name="input">The current input state.</param> /// <returns>Not sure yet.</returns> public virtual bool PerformKeyEvent(Keys[] keys, InpState input) { if (StopOnTab && Parent != null && !input.OldKey.IsKeyDown(Keys.Tab) && input.Key.IsKeyDown(Keys.Tab)) { Container prnt = (Container)Parent; int index = prnt.Controls.IndexOf(this); // tab back if (input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)) { for (int i = index - 1; i >= 0; i--) { if (prnt.Controls[i].StopOnTab) { prnt.Controls[i].Focus(); return true; } } for (int i = prnt.Controls.Count - 1; i > index; i--) { if (prnt.Controls[i].StopOnTab) { prnt.Controls[i].Focus(); return true; } } } else // tab forward { for (int i = index + 1; i < prnt.Controls.Count; i++) { if (prnt.Controls[i].StopOnTab) { prnt.Controls[i].Focus(); return true; } } for (int i = 0; i < index; i++) { if (prnt.Controls[i].StopOnTab) { prnt.Controls[i].Focus(); return true; } } } } return false; }
/// <summary>Performs a key event on this Control.</summary> /// <param name="keys">The pressed keys.</param> /// <param name="input">The current input state.</param> /// <returns>Not sure yet.</returns> public override bool PerformKeyEvent(Keys[] keys, InpState input) { if (base.PerformKeyEvent(keys, input)) return true; DateTime now = DateTime.Now; cursBlinkOffset = now.Millisecond; bool shiftDown = (input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)); foreach (Keys key in keys) { string insertChar = null; bool sameKey = input.OldKey.IsKeyDown(key); // make sure the key doesn't stick if (sameKey & ((now - lastPressedKey).TotalSeconds < .5 || (now - lastKeyEvent).Milliseconds < 64)) continue; if (!sameKey) lastPressedKey = now; // Ctrl + X/C/V if ((input.Key.IsKeyDown(Keys.LeftControl) || input.Key.IsKeyDown(Keys.RightControl)) && (key == Keys.X || key == Keys.C || key == Keys.V)) { switch (key) { case Keys.X: // cut if (selectedFromIndex != -1 && selectedFromIndex != editIndex) { int from = Math.Min(editIndex, selectedFromIndex); int to = Math.Max(editIndex, selectedFromIndex); System.Windows.Forms.Clipboard.SetText(text.ToString(from, to - from)); // delete selected text text.Remove(from, to - from); editIndex = from; selectedFromIndex = -1; updateEditPosition(); if (Font.MeasureString(text).X < (float)Width) ScrollValueH = 0f; else keepCursorInView(); refreshDesiredEditPositionX(); } break; case Keys.C: // copy if (selectedFromIndex != -1 && selectedFromIndex != editIndex) { int from = Math.Min(editIndex, selectedFromIndex); int to = Math.Max(editIndex, selectedFromIndex); System.Windows.Forms.Clipboard.SetText(text.ToString(from, to - from)); } break; case Keys.V: // paste if (System.Windows.Forms.Clipboard.ContainsText()) { // remove invalid special characters StringBuilder buf = new StringBuilder(System.Windows.Forms.Clipboard.GetText()); for (int i = 0; i < buf.Length; i++) { if (!(AllowAlpha && ((buf[i] >= 'a' && buf[i] <= 'z') || (buf[i] >= 'A' && buf[i] <= 'Z'))) && !(AllowNumeric && buf[i] >= '0' && buf[i] <= '9') && !(AllowMultiLine && buf[i] == '\n') && !((AllowedSpecChars == null && "!@#$%^&*() .-,\"\';|\\?/".Contains(buf[i])) || (AllowedSpecChars != null && AllowedSpecChars.Contains(buf[i])))) { buf.Remove(i, 1); i--; } } insertChar = buf.ToString(); } break; } } // A-Z else if (key >= Keys.A && key <= Keys.Z) // alpha { if (AllowAlpha) { insertChar = shiftDown ? ((char)key).ToString() : ((char)(key - Keys.A + 'a')).ToString(); } } // 0-9 else if (key >= Keys.D0 && key <= Keys.D9) { bool usedSpec = false; if (shiftDown) { char ch; switch (key) { case Keys.D1: ch = '!'; break; case Keys.D2: ch = '@'; break; case Keys.D3: ch = '#'; break; case Keys.D4: ch = '$'; break; case Keys.D5: ch = '%'; break; case Keys.D6: ch = '^'; break; case Keys.D7: ch = '&'; break; case Keys.D8: ch = '*'; break; case Keys.D9: ch = '('; break; default: // Keys.D0: ch = ')'; break; } if (isSpecCharAllowed(ch)) { usedSpec = true; insertChar = ch.ToString(); } } if (!usedSpec && AllowNumeric) insertChar = ((char)(key - Keys.D0 + '0')).ToString(); } else { switch (key) { // [space] case Keys.Space: if (isSpecCharAllowed(' ')) insertChar = ((char)key).ToString(); break; // . or > case Keys.OemPeriod: if (shiftDown && isSpecCharAllowed('>')) insertChar = ">"; else if (isSpecCharAllowed('.')) insertChar = "."; break; // - or _ case Keys.OemMinus: if (shiftDown && isSpecCharAllowed('_')) insertChar = "_"; else if (isSpecCharAllowed('-')) insertChar = "-"; break; // , or < case Keys.OemComma: if (shiftDown && isSpecCharAllowed('<')) insertChar = "<"; else if (isSpecCharAllowed(',')) insertChar = ","; break; // ' or " case Keys.OemQuotes: if (shiftDown && isSpecCharAllowed('\"')) insertChar = "\""; else if (isSpecCharAllowed('\'')) insertChar = "\'"; break; // ; or : case Keys.OemSemicolon: if (shiftDown && isSpecCharAllowed(':')) insertChar = ":"; else if (isSpecCharAllowed(';')) insertChar = ";"; break; // \ or | case Keys.OemPipe: if (shiftDown && isSpecCharAllowed('|')) insertChar = "|"; else if (isSpecCharAllowed('\\')) insertChar = "\\"; break; // / or ? case Keys.OemQuestion: if (shiftDown && isSpecCharAllowed('?')) insertChar = "?"; else if (isSpecCharAllowed('/')) insertChar = "/"; break; // [ or { case Keys.OemOpenBrackets: if (shiftDown && isSpecCharAllowed('{')) insertChar = "{"; else if (isSpecCharAllowed('[')) insertChar = "["; break; // ] or } case Keys.OemCloseBrackets: if (shiftDown && isSpecCharAllowed('}')) insertChar = "}"; else if (isSpecCharAllowed(']')) insertChar = "]"; break; // = or + case Keys.OemPlus: if (shiftDown && isSpecCharAllowed('+')) insertChar = "+"; else if (isSpecCharAllowed('=')) insertChar = "="; break; // ` or ~ case Keys.OemTilde: if (shiftDown && isSpecCharAllowed('~')) insertChar = "~"; else if (isSpecCharAllowed('`')) insertChar = "`"; break; // [left] case Keys.Left: if (selectedFromIndex != -1 && selectedFromIndex != editIndex && !shiftDown) editIndex = Math.Min(editIndex, selectedFromIndex) + 1; refreshSelectedFromIndex(input); if (editIndex > 0) { editIndex--; updateEditPosition(); refreshDesiredEditPositionX(); } break; // [right] case Keys.Right: if (selectedFromIndex != -1 && selectedFromIndex != editIndex && !shiftDown) editIndex = Math.Max(editIndex, selectedFromIndex) - 1; refreshSelectedFromIndex(input); if (editIndex < text.Length) { editIndex++; updateEditPosition(); refreshDesiredEditPositionX(); } break; // [up] case Keys.Up: { if (selectedFromIndex != -1 && selectedFromIndex != editIndex && !shiftDown) { editIndex = Math.Min(editIndex, selectedFromIndex); refreshDesiredEditPositionX(); } refreshSelectedFromIndex(input); int begPrev = 0; int begThis = -1; for (int i = editIndex - 1; i > 0; i--) { if (text[i] == '\n') { if (begThis == -1) begThis = i + 1; else { begPrev = i + 1; break; } } } if (begThis != -1) { float lWidth = desiredEditPositionX; editIndex = begThis - 1; for (int i = begPrev; i < begThis - 1; i++) { float tWidth = Font.MeasureString(text.ToString(begPrev, i - begPrev + 1)).X; if (tWidth > lWidth) { float cWidth = Font.MeasureString(text[i].ToString()).X; editIndex = (lWidth > tWidth - cWidth / 2f) ? (i + 1) : i; break; } } } else { editIndex = 0; } updateEditPosition(); } break; // [down] case Keys.Down: { if (selectedFromIndex != -1 && selectedFromIndex != editIndex && !shiftDown) { editIndex = Math.Max(editIndex, selectedFromIndex); refreshDesiredEditPositionX(); } refreshSelectedFromIndex(input); int begNext = -1; int endNext = text.Length - 1; int begThis = 0; for (int i = editIndex - 1; i > 0; i--) { if (text[i] == '\n') { begThis = i + 1; break; } } for (int i = editIndex; i < text.Length; i++) { if (text[i] == '\n') { if (begNext == -1) { begNext = i + 1; } else { endNext = i - 1; break; } } } if (begNext != -1) { float lWidth = desiredEditPositionX; editIndex = endNext + 1; for (int i = begNext; i <= endNext; i++) { float tWidth = Font.MeasureString(text.ToString(begNext, i - begNext + 1)).X; if (tWidth > lWidth) { float cWidth = Font.MeasureString(text[i].ToString()).X; editIndex = (lWidth > tWidth - cWidth / 2f) ? (i + 1) : i; break; } } } else { editIndex = text.Length; } updateEditPosition(); } break; // [backspace] case Keys.Back: { bool successful = false; if (selectedFromIndex != -1) { int from = Math.Min(editIndex, selectedFromIndex); int to = Math.Max(editIndex, selectedFromIndex); text.Remove(from, to - from); editIndex = from; selectedFromIndex = -1; successful = true; } else if (editIndex > 0) { text.Remove(editIndex - 1, 1); editIndex--; successful = true; } if (successful) { updateEditPosition(); if (Font.MeasureString(text).X < (float)Width) ScrollValueH = 0f; else keepCursorInView(); refreshDesiredEditPositionX(); } break; } // [delete] case Keys.Delete: if (selectedFromIndex != -1) { int from = Math.Min(editIndex, selectedFromIndex); int to = Math.Max(editIndex, selectedFromIndex); text.Remove(from, to - from); editIndex = from; selectedFromIndex = -1; updateEditPosition(); if (Font.MeasureString(text).X < (float)Width) ScrollValueH = 0f; else keepCursorInView(); refreshDesiredEditPositionX(); } else if (editIndex < text.Length) { text.Remove(editIndex, 1); if (Font.MeasureString(text).X < (float)Width) ScrollValueH = 0f; refreshDesiredEditPositionX(); } break; // [home] case Keys.Home: refreshSelectedFromIndex(input); if (editIndex > 0) { for (; editIndex > 0 && text[editIndex - 1] != '\n'; editIndex--) { } updateEditPosition(); refreshDesiredEditPositionX(); } break; // [end] case Keys.End: refreshSelectedFromIndex(input); if (editIndex < text.Length) { for (; editIndex < text.Length && text[editIndex] != '\n'; editIndex++) { } updateEditPosition(); refreshDesiredEditPositionX(); } break; // [enter] case Keys.Enter: if (AllowMultiLine) insertChar = "\n"; break; // [tab] case Keys.Tab: if (!StopOnTab) { if (selectedFromIndex != -1 && selectedFromIndex != editIndex) { bool editIndexFirst = editIndex <= selectedFromIndex; int index = editIndexFirst ? editIndex : selectedFromIndex; for (; index > 0 && text[index - 1] != '\n'; index--) { } int spaceCount = 0; for (int i = index; i < text.Length && text[i] == ' '; i++) { spaceCount++; } int from; int to; if (editIndexFirst) { from = editIndex; to = selectedFromIndex; } else { from = selectedFromIndex; to = editIndex; } if (shiftDown) // shift back { int toRemove = 0; if (spaceCount > 0) { toRemove = ((spaceCount - 1) % 3) + 1; text.Remove(index, toRemove); editIndex -= toRemove; selectedFromIndex -= toRemove; from -= toRemove; to -= toRemove; } for (; to < text.Length && text[to] != '\n'; to++) { } for (int i = from + 1; i < to; i++) { if (text[i - 1] == '\n') { spaceCount = 0; int j = i; for (; j < text.Length && text[j] == ' '; j++) { } spaceCount = j - i; if (spaceCount > 0) { toRemove = ((spaceCount - 1) % 3) + 1; text.Remove(i, toRemove); if (editIndexFirst) selectedFromIndex -= toRemove; else editIndex -= toRemove; to -= toRemove; } } } updateEditPosition(); if (Font.MeasureString(text).X < (float)Width) ScrollValueH = 0f; else keepCursorInView(); refreshDesiredEditPositionX(); } else // shift forward { string toInsert = "".PadRight(3 - (spaceCount % 3)); text.Insert(index, toInsert); editIndex += toInsert.Length; selectedFromIndex += toInsert.Length; from += toInsert.Length; to += toInsert.Length; for (; to < text.Length && text[to] != '\n'; to++) { } for (int i = from + 1; i < to; i++) { if (text[i - 1] == '\n') { spaceCount = 0; int j = i; for (; j < text.Length && text[j] == ' '; j++) { } spaceCount = j - i; toInsert = "".PadRight(3 - (spaceCount % 3)); text.Insert(i, toInsert); if (editIndexFirst) selectedFromIndex += toInsert.Length; else editIndex += toInsert.Length; to += toInsert.Length; } } updateEditPosition(); refreshDesiredEditPositionX(); } } else { int lineIndex = 0; for (int i = editIndex - 1; i >= 0 && text[i] != '\n'; i--) { lineIndex++; } insertChar = "".PadRight(3 - (lineIndex % 3)); } } break; } } // insert character if (insertChar != null) { if (selectedFromIndex != -1 && selectedFromIndex != editIndex) { int from = Math.Min(editIndex, selectedFromIndex); int to = Math.Max(editIndex, selectedFromIndex); text.Remove(from, to - from); editIndex = from; } text.Insert(editIndex, insertChar); editIndex += insertChar.Length; updateEditPosition(); if (selectedFromIndex != -1) { if (Font.MeasureString(text).X < (float)Width) ScrollValueH = 0f; else keepCursorInView(); } refreshDesiredEditPositionX(); selectedFromIndex = -1; } keepCursorInView(); refreshHighlights(); lastKeyEvent = now; } return true; }
/// <summary>Performs the specified mouse event on this control.</summary> /// <param name="evnt">The event to perform.</param> /// <param name="cursorPos">The position of the cursor at the time of the event.</param> /// <param name="input">The current input state.</param> /// <returns>Whether or not the event was performed.</returns> public override bool PerformMouseEvent(Desktop.Event evnt, Point cursorPos, InpState input) { if (evnt == Desktop.Event.MouseLeftDown) { cursBlinkOffset = DateTime.Now.Millisecond; if (input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)) { if (selectedFromIndex == -1) selectedFromIndex = editIndex; } else { selectedFromIndex = -1; } editIndex = getCharPos((float)(cursorPos.X - Left), (float)(cursorPos.Y - Top)); updateEditPosition(); refreshDesiredEditPositionX(); refreshHighlights(); } return base.PerformMouseEvent(evnt, cursorPos, input); }
/// <summary>To be called at the beginning of certain key event.</summary> /// <param name="input">The current input state.</param> private void refreshSelectedFromIndex(InpState input) { if (!input.Key.IsKeyDown(Keys.LeftShift) && !input.Key.IsKeyDown(Keys.RightShift)) selectedFromIndex = -1; else if (selectedFromIndex == -1) selectedFromIndex = editIndex; }
/// <summary>Performs the specified mouse event on this control.</summary> /// <param name="evnt">The event to perform.</param> /// <param name="cursorPos">The position of the cursor at the time of the event.</param> /// <param name="input">The current input state.</param> /// <returns>Whether or not the event was performed.</returns> public override bool PerformMouseEvent(Desktop.Event evnt, Point cursorPos, InpState input) { if (evnt == Desktop.Event.MouseLeftDown) { editIndex = getCharPos((float)(cursorPos.X - Left)); updateEditPosition(); } return base.PerformMouseEvent(evnt, cursorPos, input); }
/// <summary>Performs a key event on this Control.</summary> /// <param name="keys">The pressed keys.</param> /// <param name="input">The current input state.</param> /// <returns>Not sure yet.</returns> public override bool PerformKeyEvent(Keys[] keys, InpState input) { if (base.PerformKeyEvent(keys, input)) return true; foreach (Keys key in keys) { // make sure the key doesn't stick if (input.OldKey.IsKeyDown(key)) continue; // A-Z if ((key >= Keys.A && key <= Keys.Z)) // alpha { if (AllowAlpha) { string str = null; if (input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)) str = ((char)key).ToString(); else str = ((char)(key - Keys.A + 'a')).ToString(); text.Insert(editIndex, str); editIndex++; updateEditPosition(); } } // 0-9 else if (key >= Keys.D0 && key <= Keys.D9) { bool usedSpec = false; if (input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)) { char ch; switch (key) { case Keys.D1: ch = '!'; break; case Keys.D2: ch = '@'; break; case Keys.D3: ch = '#'; break; case Keys.D4: ch = '$'; break; case Keys.D5: ch = '%'; break; case Keys.D6: ch = '^'; break; case Keys.D7: ch = '&'; break; case Keys.D8: ch = '*'; break; case Keys.D9: ch = '('; break; default: // Keys.D0: ch = ')'; break; } if (AllowedSpecChars == null || AllowedSpecChars.Contains(ch)) { usedSpec = true; text.Insert(editIndex, ch.ToString()); editIndex++; updateEditPosition(); } } if (!usedSpec && AllowNumeric) { text.Insert(editIndex, ((char)(key - Keys.D0 + '0')).ToString()); editIndex++; updateEditPosition(); } } // [space] else if (key == Keys.Space) { if (AllowedSpecChars == null || AllowedSpecChars.Contains(' ')) { text.Insert(editIndex, ((char)key).ToString()); editIndex++; updateEditPosition(); } } // . else if (key == Keys.OemPeriod) { if (AllowedSpecChars == null || AllowedSpecChars.Contains('.')) { text.Insert(editIndex, '.'); editIndex++; updateEditPosition(); } } // - else if (key == Keys.OemMinus) { if (AllowedSpecChars == null || AllowedSpecChars.Contains('-')) { text.Insert(editIndex, '-'); editIndex++; updateEditPosition(); } } // , else if (key == Keys.OemComma) { if (AllowedSpecChars == null || AllowedSpecChars.Contains(',')) { text.Insert(editIndex, ','); editIndex++; updateEditPosition(); } } // ' or " else if (key == Keys.OemQuotes) { if ((input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)) && (AllowedSpecChars == null || AllowedSpecChars.Contains('\"'))) { text.Insert(editIndex, '\"'); editIndex++; updateEditPosition(); } else if (AllowedSpecChars == null || AllowedSpecChars.Contains('\'')) { text.Insert(editIndex, '\''); editIndex++; updateEditPosition(); } } // ; else if (key == Keys.OemSemicolon) { if (AllowedSpecChars == null || AllowedSpecChars.Contains(';')) { text.Insert(editIndex, ';'); editIndex++; updateEditPosition(); } } // \ or | else if (key == Keys.OemPipe) { if ((input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)) && (AllowedSpecChars == null || AllowedSpecChars.Contains('|'))) { text.Insert(editIndex, '|'); editIndex++; updateEditPosition(); } else if (AllowedSpecChars == null || AllowedSpecChars.Contains('\\')) { text.Insert(editIndex, '\\'); editIndex++; updateEditPosition(); } } // / or ? else if (key == Keys.OemQuestion) { if ((input.Key.IsKeyDown(Keys.LeftShift) || input.Key.IsKeyDown(Keys.RightShift)) && (AllowedSpecChars == null || AllowedSpecChars.Contains('?'))) { text.Insert(editIndex, '?'); editIndex++; updateEditPosition(); } else if (AllowedSpecChars == null || AllowedSpecChars.Contains('/')) { text.Insert(editIndex, '/'); editIndex++; updateEditPosition(); } } // [left] else if (key == Keys.Left) { if (editIndex > 0) { editIndex--; updateEditPosition(); } } // [right] else if (key == Keys.Right) { if (editIndex < text.Length) { editIndex++; updateEditPosition(); } } // [backspace] else if (key == Keys.Back) { if (editIndex > 0) { text.Remove(editIndex - 1, 1); editIndex--; updateEditPosition(); if (Font.MeasureString(text).X < (float)Width) ScrollValue = 0f; else keepCursorInView(); } } // [delete] else if (key == Keys.Delete) { if (editIndex < text.Length) { text.Remove(editIndex, 1); if (Font.MeasureString(text).X < (float)Width) ScrollValue = 0f; } } // [home] else if (key == Keys.Home) { if (editIndex > 0) { editIndex = 0; updateEditPosition(); } } // [end] else if (key == Keys.End) { if (editIndex < text.Length) { editIndex = text.Length; updateEditPosition(); } } } keepCursorInView(); return true; }
/// <summary>Performs the specified mouse event on this control.</summary> /// <param name="evnt">The event to perform.</param> /// <param name="cursorPos">The position of the cursor at the time of the event.</param> /// <param name="input">The current input state.</param> /// <returns>Whether or not the event was performed.</returns> public override bool PerformMouseEvent(Event evnt, Point cursorPos, InpState input) { switch (evnt) { case Event.MouseLeftUp: case Event.MouseRightUp: // update mouse state if (evnt == Event.MouseLeftUp) IsMouseLeftDown = false; else IsMouseRightDown = false; // hide the menu if (CurMenu != null && CurMenu.Parent != Focused) hideMenu(); // event if (Focused != null) { Point cPos = cursorPos - Focused.getScreenPos() + Focused.Location; if (!Focused.inBounds(cPos) || !Focused.PerformMouseEvent(evnt, cPos, input)) hideMenu(); return true; } hideMenu(); break; case Event.MouseLeftDown: case Event.MouseRightDown: // update mouse state DateTime now = DateTime.UtcNow; if (evnt == Event.MouseLeftDown) { IsMouseLeftDown = true; if (Hovered != null && Hovered.AcceptDoubleClicks && Hovered == lastControlPressed && now - lastTimePressed <= DoubleClickThreshold) evnt = Event.MouseLeftDoubleClick; } else { IsMouseRightDown = true; if (Hovered != null && Hovered.AcceptDoubleClicks && Hovered == lastControlPressed && now - lastTimePressed <= DoubleClickThreshold) evnt = Event.MouseRightDoubleClick; } PopUpMenu menu = CurMenu; // event bool result = false; if (Hovered != null) result = Hovered.PerformMouseEvent(evnt, cursorPos - Hovered.getScreenPos() + Hovered.Location, input); // if menu didn't change, hide it if (CurMenu != null && menu == CurMenu) hideMenu(); // nothing was clicked if (!result) { Focused = null; lastControlPressed = null; } else { lastControlPressed = (evnt == Event.MouseLeftDoubleClick || evnt == Event.MouseRightDoubleClick) ? null : Hovered; lastTimePressed = now; } return result; default: throw new Exception("Unrecognized event: " + evnt.ToString()); } return false; }
/// <summary>Performs a key event on this Control.</summary> /// <param name="keys">The pressed keys.</param> /// <param name="input">The current input state.</param> /// <returns>Whether or not the desktop claimed the key presses.</returns> public override bool PerformKeyEvent(Keys[] keys, InpState input) { if (Focused == null) return false; return Focused.PerformKeyEvent(keys, input); }