Горячие клавиши
Горячие клавиши¶
Команду плитки также можно вызвать по хоткею. Хоткей добавляется в коллекцию IUIContext.InputBindings и работает, пока активен контекст, т.е. выделена вкладка, связанная с контекстом. Добавить хоткей можно также в любой момент, когда уже есть контекст, но лучше это делать в расширении на открытие вкладки InitializingLocal:
ITile saveCard = context.Workspace.LeftPanel.TryGet("SaveCard");
if (saveCard != null)
{
saveCard.Context.AddKeyBinding(saveCard, Key.S, ModifierKeys.Control);
}
Или так:
public static readonly KeyGesture SaveCardKey = new KeyGesture(Key.S, ModifierKeys.Control, "Ctrl+S");
...
ITile saveCard = context.Workspace.LeftPanel.TryGet("SaveCard");
if (saveCard != null)
{
saveCard.Context.AddInputBinding(saveCard, SaveCardKey);
}
Плитка создаётся в расширениях InitializingGlobal или InitializingLocal, причём для неё можно добавить ToolTip с указанием горячей клавиши.
ITile tile = new Tile("SaveCard" , "Сохранить", ...,
toolTip: TileHelper.GetToolTip("Сохраняет карточку", SaveCardKey));
Нужно сделать так, чтобы в состоянии плитки IsEnabled = false хоткей перестал работать. Поэтому управлять состоянием IsEnabled напрямую в расширении открытия панели OpeningLocal не рекомендуется. Вместо этого используется специальное событие Evaluating.
Событие Evaluating¶
Смысл Evaluating – вычислить текущие значения IsEnabled, IsCollapsed и IsHidden, и опционально их применить. Дизейбл и скрытие имеют более высокий приоритет, чем обратные значения, поэтому если хотя бы один обработчик установит SetIsEnabled(false) или SetIsEnabledWithCollapsing(false), то плитка будет дизейблена. Если состояние IsEnabled плитки определяется исключительно через Evaluating и не задаётся явно через свойство, то в любой момент (без расширений на открытие панели) можно сказать, будет ли плитка дизейблена, и, соответственно, будет ли отключён хоткей.
Событие рекомендуется добавлять в расширениях InitializingGlobal или InitializingLocal.
tile1.Evaluating += (s, e) => e.SetIsEnabledWithCollapsing(this.session.User.IsAdministrator); // скрываем и дизейблим плитку для не-админов
tile2.Evaluating += (s, e) => e.SetIsEnabled(this.session.User.IsAdministrator); // дизейблим плитку для не-админов, но не скрываем её в панели
tile3.Evaluating += TileHelper.DisableTileWithCollapsingHandler; // навсегда скрываем и дизейблим плитку (для текущей вкладки)
tile3.Evaluating += TileHelper.DisableTileHandler; // навсегда дизейблим плитку без скрытия (для текущей вкладки)
Также это можно делать в конструкторе плитки, а позднее, в другом расширении, можно добавить ещё несколько обработчиков.
ITile saveCard = new Tile("SaveCard", "Сохранить", ...,
evaluating: (s, e) => ...);
В самом начале цепочки расширений при открытии панели OpeningLocal будут вычислены и заданы значения указанных свойств для всех отображаемых плиток в панели следующим образом:
TileEvaluationResult result = await context.Panel.EvaluateAsync();
result.Apply();
Можно вычислить актуальные значения для конкретной плитки tile, не изменяя сами свойства:
ITile tile = ... ;
TileEvaluationResult result = await tile.EvaluateSelfAsync();
bool shouldBeEnabled = result.EventArgs.GetIsEnabledEffective(tile);
Пример расширения на плитку “создание резолюци蔶
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using Tessa.Cards;
using Tessa.UI;
using Tessa.UI.Cards;
using Tessa.UI.Tiles;
using Tessa.UI.Tiles.Extensions;
namespace Tessa.Extensions.Client.Tiles
{
public sealed class SendTaskTileExtension : TileExtension
{
private static void EnableOnUpdateAndAllowTasks(object sender, TileEvaluationEventArgs e)
{
// плитка будет доступна, если карточка хотя бы раз была сохранена (Update)
// и для типа карточки разрешены задания (AllowTasks)
ICardEditorModel editor = e.CurrentTile.Context.CardEditor;
ICardModel model;
e.SetIsEnabledWithCollapsing(
e.CurrentTile,
editor != null
&& (model = editor.CardModel) != null
&& model.CardType.Flags.Has(CardTypeFlags.AllowTasks)
&& model.Card.StoreMode == CardStoreMode.Update);
}
private static async void SendTaskAction(object parameter)
{
// действие по созданию резолюции, выполняется асинхронно
IUIContext context = UIContext.Current;
ICardEditorModel editor;
ICardModel model;
if ((editor = context.CardEditor) != null
&& !editor.OperationInProgress
&& (model = editor.CardModel) != null)
{
if (await model.HasChangesAsync())
{
await editor.SaveCardAsync(context);
}
await editor.OpenCardAsync(
model.Card.ID,
model.CardType.ID,
model.CardType.Name,
context,
new Dictionary<string, object> { { "need_starting", true } });
}
}
// горячая клавиша на создание резолюции
private static readonly KeyGesture sendTaskKey =
new KeyGesture(Key.R, ModifierKeys.Control | ModifierKeys.Shift, "Ctrl+Shift+R");
public override Task InitializingGlobal(ITileGlobalExtensionContext context)
{
// плитка добавляется с обработчиком Evaluating, указанном в конструкторе
// в расширениях InitializingLocal для специальных видов вкладок, таких как просмотр удалённой карточки,
// плитку можно будет принудительно скрыть, добавив обработчик tile.Evaluating += TileHelper.DisableTileWithCollapsingHandler
context.Workspace.LeftPanel.Tiles.Add(
new Tile(
"SendTask",
"Создать резолюцию",
context.Icons.Get("Thin91"),
context.Workspace.LeftPanel,
new DelegateCommand(SendTaskAction),
TileGroups.Cards,
order: 100,
toolTip: TileHelper.GetToolTip("Создаёт задание с параметрами резолюции", sendTaskKey.DisplayString),
evaluating: EnableOnUpdateAndAllowTasks));
return Task.CompletedTask;
}
public override Task InitializingLocal(ITileLocalExtensionContext context)
{
// добавляем горячую клавишу, которая будет работать только тогда, когда плитка не дизейблена
ITile sendTask = context.Workspace.LeftPanel.Tiles["SendTask"];
sendTask.Context.AddInputBinding(sendTask, sendTaskKey);
return Task.CompletedTask;
}
}
}
Регистрация расширения:
extensionContainer
.RegisterExtension<ITileGlobalExtension, SendTaskTileExtension>(x => x
.WithOrder(ExtensionStage.AfterPlatform)
.WithSingleton<SendTaskTileExtension>());
Скрытие системных плиток¶
Все плитки, которые автоматически создаются платформой, могут быть скрыты пользовательскими расширениями по определённым условиям (или скрыты всегда), причём они могут быть найдены по именам в классе Tessa.UI.Tiles.TileNames. Для них точно так же надо написать расширение, запрещающее их отображать, добавив соответствующую подписку на событие Evaluating на уровне расширения CardUIExtension/InitializingLocal.
Приведём пример расширения, скрывающего плитку “Договор”, создающую одноимённый тип карточки. Однако, отметим, что если речь про конкретно типы карточек, то в системе для них имеются готовые настройки (в TessaAdmin при редактировании типа). Флажок Hidden скрывает тип карточки ото всех пользователей, при этом он по-прежнему может быть создан, но только системными средствами. Флажок Administrative запрещает создание и работу с типом карточки любым пользователям, кроме администраторов, при этом плитка создания типа тоже скрывается. Если нужно учесть некоторое более сложное условие (например, какая именно вкладка открыта в настоящий момент), то подходит решение, предложенное в примере ниже.
using System.Threading.Tasks;
using Tessa.Platform;
using Tessa.UI.Tiles;
using Tessa.UI.Tiles.Extensions;
public sealed class HideContractTileExtension : TileExtension
{
public override Task InitializingLocal(ITileLocalExtensionContext context)
{
ITilePanel panel = context.Workspace.RightPanel;
// при необходимости здесь можно проверить текущий контекст IUIContext context = panel.Context;
// получаем плитку "Создать карточку"
ITile createCard = panel.Tiles.TryGet(TileNames.CreateCard);
if (createCard != null)
{
// на этом уровне плитки можно получить по имени группы Group, указанной в TessaAdmin для типа карточки
// получаем плитку "Документы"
ITile documents = createCard.Tiles.TryGet(TileNames.DocumentTypes);
if (documents != null)
{
// на этом уровне плитки можно получить по имени типа карточки (поле Name для типа карточки в TessaAdmin)
// получаем плитку "Договор"
ITile contract = documents.Tiles.TryGet("Contract");
if (contract != null)
{
// скрываем этот тип и выполняем Disable
// т.е. даже если с плиткой ассоциирован хоткей, он прекратит работать
contract.Evaluating += TileHelper.DisableTileWithCollapsingHandler;
}
}
}
return Task.CompletedTask;
}
}