Re[3]: WPF Hint
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.11.09 17:53
Оценка:
Здравствуйте, capgoat, Вы писали:

C>Что ж, давай будем сливать.

C>Но уточним несколько моментов:
C>1. Нужен ли тэг <param name=.../>, и если да, то что в нем изменить или оставить как есть?

Скажем так. Он нужен но немного в другом виде. Ниже я приведу полный список уточненных требований. В данном случае нам нужно обобщить данный тег. Назовем его просто <row>, а внешний тег <tabulated>. Дело в том, что он может применяться не только для параметров.

C>2. "Табличное" форматирование — пара юзкейсов не помешало бы, должно быть статическим (как в html) или динамическим (как с параметрами)?


ОК. Ниже будет пример.

C>3. <hint>

C>На самом деле разметку можно засунуть и в атрибут, например так: "<a " + new XAttribute("attr", "<b>bb</b>").ToString() + ">aa</a>".

Это не удобно, так как внутри так же будут встречаться кавычки. Тем более, что я уже подправил исходную реализацию и она уже обеспечивает требуемое поведение.

C>А подгрузку хинта я бы наверное сделал через событие, к-е возникало бы при наведении мышки на активный участок, примерно так:


Это я уже сделал, и не через событие, а просто через делегат. Так значительно удобнее. И вообще, всю динамически меняемую информацию удобнее задавать через параметры метода Show, а не через свойства. В свойствах должны быть общие настройки вроде шрифтов используемых по умолчанию.

C>
C>        // PreviewHint event handler
C>        void OnPreviewHint(PreviewHintArgs args)
C>        {
C>            // analyze args.Key
C>            // if (valid)
C>            // {
C>            //  args.HintText = ...
C>            //  args.ShowHint = true
C>            // }
C>            // else            
C>            //  args.ShowHint = false
C>        }


Для сравнения, как это делается сейчас:
if (Service.ShowHint(view, hintSpan, tipInfo.GetHintContent, hintText))
  return (int)TipSuccesses2.TIP_S_NODEFAULTTIP;

При этом код формирующий функцию находится в совершенно другом месте (даже другой сборке):
    public GetDelayedHintHendler() : Func[string, string]
    {
      def makeTypeStr(key : string) : string
      {
        AddNamespaces  = true;
        ExpandTypeArgs = false;
        def ty = _idToTypeMap[int.Parse(key)];
        
        def makeDelegateExt(ti)
        {
          def m = ti.LookupMember("Invoke").Head;
          "<lb/>Signature: " + FixedTypeToString(m.GetMemType()) + "<lb/>"
          + "Inferred: " + TypeVarToString(ty.TypeOfMember(m))
        }
        
        def baseTypesExt(ti : TypeInfo, baseTypeName = "Base type") : string
        {
          def implementsInfo(itfs : list[MType.Class]) : string
          {
            def plural = if (itfs.Length > 1) "s" else "";
            def prompt = if (ti.IsInterface) $"Base interface$plural" else "Implements";
            if (itfs.IsEmpty) ""
            else $<#<lb/>$prompt: ..$(itfs; ", "; FixedClassTypeToString)#>
          }
          def old = AddNamespaces;
          AddNamespaces = false;
          def itfs1 = ti.GetDirectSuperTypes();
          def baseType = ti.BaseType;
          def itfs = if (baseType == null) itfs1 else itfs1.Tail;
          def res = 
            if (baseType != null)
              $"<lb/>$baseTypeName: " + TypeVarToString(ty.GetInstantiatedSuperType(baseType)) + implementsInfo(itfs)
            else implementsInfo(itfs);
          
          AddNamespaces = old;
          
          res
        }
        
        def (kind, ext) = 
          match (ty)
          {
            | Class(ti, _args) => 
              def (kind, ext) =
                if      (ti.IsDelegate)  ("delegate ", makeDelegateExt(ti))
                else if (ti.IsModule)    ("module ", "")
                else if (ti.IsValueType) ("value type ", "")
                else match (ti.GetTydecl())
                {
                  | Class     with kind = "class "
                  | Interface with kind = "interface " => (kind, baseTypesExt(ti))
                  | VariantOption => 
                    def old = AddNamespaces;
                    AddNamespaces = false;
                    def flds = ti.GetFields(BFlg.DeclaredOnly | BFlg.Instance | BFlg.Public)
                                 .Map(f => $"$(f.Name) : $(TypeVarToString(ty.TypeOfMember(f)));");
                    def flds = if (flds.IsEmpty) "" else  $<#<lb/>Fields: ..$flds#>;
                    AddNamespaces = old;
                    ("variant option ", flds + baseTypesExt(ti, "Declaring variant"))
                      
                  | Variant(members) when members.IsEmpty => ("variant ", baseTypesExt(ti))
                  | Variant(members) =>
                    def old = AddNamespaces;
                    AddNamespaces = false;
                    def ops = members.Map(o => FixedClassTypeToString(o.GetMemType()));
                    AddNamespaces = old;
                    ("variant ", $<#<lb/>Variant options: ..$ops<lb/>$(baseTypesExt(ti))#>)
                    
                  | Enum              => ("enum ", "")
                  | Alias             => assert(false);
                };
              def obsolete = if (ti.IsObsolete) "[Obsolete] " else "";

              (obsolete + "<keyword>" + Utils.MakeAccessModifiers(ti) + kind + "</keyword>", ext)
            
            | TyVarRef       => ("type parameter ", "")
            | Fun            => ("function type ", "")
            | Tuple          => ("tuple ", "")
            | Array(_, rank) => ($"$rank dimention ", "")
            | Ref            => ("ref parameter ", "")
            | Out            => ("out parameter ", "")
            | Void           => ("", "")
            | Intersection   => ("", "")
          };
        
        $"<hint>$kind$(FixedTypeToString(ty))$ext</hint>"
      }
      
      makeTypeStr
    }


C>        class PreviewHintArgs
C>        {
C>            public string Key { get; private set; }
C>            public string HintText { get; set; }
C>            public bool ShowHint { get; set; }
C>        }
C>


Вот это все совсем лишнее! События — это навязанная Майкрософтом глупость. Целая идеология навернута на пустом месте. Функции высшего порядка проще и удобнее. Со временем это поймут и в Майкрософт... я надеюсь. Врос просто. На входе ключ, на выходе значение. Как там что внутри устроено — это детали реализации. Никакие другие параметры не нужны.
И главное! Имеративщине без особой надобности — это зло! Не надо делать модифицируемые объекты когда можно просто возвратить текст из функции.

C>4. Первая загрузка хинта

C>Мне кажется можно проинициализировать WPF, создав например простое невидимое окно и сразу его закрыв, при старте nemerle.
C>5. Целая секунды до закрытия хинта мне кажется многовато(к слову, введение этого таймера была вынужденная мера, из-за того, что некоторые св-ва не успевали вовремя обновляться)
C>6. CallbackOnCollectedDelegate was detected
C>Эту проблему я знаю, но по идее она не должна возникать, т.к. делегаты специально хранятся в private fields, возможно я что-то упустил.

Возникала. Но похоже я ее уже сам пофиксил. Дело было в том, что ты не сделал финализатор у своего объекта. Далее получалось следующее. Происходит какое-нибудь исключение или еще что-то и не вызывается событие в котором ты делаешь АнСабКласс от окна. Далее сборка мусора и первый же вызов оконной функции приводит к схлопыванию студии.

Кроме того оказалось, что к схлопыванию приводит любое исключение внутри оконной функции. Я поставил обработчик исключений перехваливающий все исключения долетевшие до оконной функции.
Вообще с этим перехватом проблем было много. Вроде бы я их все пофиксил. Посмотри на мои изменения:
http://code.google.com/p/nemerle/source/detail?r=8428
http://code.google.com/p/nemerle/source/detail?r=8427
http://code.google.com/p/nemerle/source/detail?r=8426
http://code.google.com/p/nemerle/source/detail?r=8425


Теперь обещанное описание требуемых возможностей


Опишу задачу.

1. Текст длинных строк в ней не переносится на другие сроки из-за чего он попросту обрезается.
2. На мой взгляд реализация у него слишком переусложнена. Происходит это, на мой взгляд, из-за того, что вместо возможностей WPF в ней используется подход который можно ласково назвать "закат солнца вручную". То есть, вместо использования контролов который форматировали бы текст в ней применяется ручной подсчет размеров (сразу оговорюсь, что в WPF я пока понимаю мало, так что могу ошибаться).

Собственно интересно послушать предложения (от самых общих, до конкретных кусков кода) о том как бы вы (естественно, те кто понимает что-то в WPF) реализовали функциональность которая будет описана ниже. Естественно, приветствуется конкретный код .

Итак, задача:

Форматирвоание

В тексте хинта разметка задается с помощью тегов а-ля XML/HTML:
<keyword></keyword> — задает синий цвет букв для своего содержимого.
<b></b> — задает жирный шрифт для своего содержимого.
<hint value='отображаемое значение' key='ключ для считывания содержимого вложенного хинта'/>
<hint value='отображаемое значение'>Содержимое вложенного хинта в котором так же возможна разметка</hint>

Тег hint определяет вложенных хинт. Значение атрибута value отображается в основном хинте. Когда к нему подводится курсор мыши появляется вложенных хинт. Содержимое вложенного хинта берется или из содерижмого тега hint (если не задан атрибут key), или из обработчика события (которому передается значение из атрибута key). Второй вариант служит для организации отложенной (ленивой) инициализации содержимого вложенных хинтов.
<type></type> — задает цвет DarkCyan букв для своего содержимого.
возможны и другие варианты. Главное что сейчас нужно понять, что разметка задается тегами.
Некоторый текст вначале
<tabulated>
  <row><col>текст первой колонки</col><col>текст второй колонки</col></row>
  <row><col>текст первой колонки второй стоки</col><col>текст второй колонки второй стоки</col></row>
  ...
</tabulated>
текст в конце

Это весьма не обычное решение. Прошу обратить на него больше внимания.
Работает оно так. Если текст всех элементов может быть размещен на одной строке (т.е. его длинна не привысит ширины экрана), то текст должен быть выведен в одну строку. Если ширина текста превышает ширину экрана, то текст должен не просто перетекать на другую строук, а автоматически выравниваться в стиле "невидимой таблицы". Чтобы стало понятнее продемонстрирую сказанное на примере. Предположим у нас есть сигнатура метода:
public GetInheritors([NotNull]filePath : string, line : int, col : int) : array [GotoInfo]

Мы размечаем ее следующим образом:
public GetInheritors(
<tabulated>
   <row><col>[NotNull]</col> <col>filePath </col> <col>: string, </col></row>
   <row><col></col>          <col>line </col>     <col>: int, </col></row>
   <row><col></col>          <col>col </col>      <col>: int</col></row>
<tabulated>
) : array [GotoInfo]

При этом внутри всего это дела могут встречаться и другие теги (теги форматирования изменяющие шрифт).
Если чистый текст полученный после удаления всех этих тегов, т.е.:
public GetInheritors([NotNull]filePath : string, line : int, col : int) : array [GotoInfo]

превышает ширину экрана (или скажем более абстрактно — некое предельное значение), то текст должен быть выведен в виде:
public GetInheritors(
  [NotNull] filePath : string, 
            line     : int, 
            col      : int
) : array [GotoInfo]/c#]

Собственно табличное форматирование нужно не только для параметров. Многие списки могли бы быть оформлены таким же образом.
<pre></pre> — тест в этом теге должен выводиться моноширинным шрифтом (в котором все символы имеют одинаковую ширину) и пробелы не должны игнорироваться. Данный формат применяется для вывода примеров кода. При этом в нем должны поддерживаться как теги форматирования (цвет и жирный), так и подхинты.

Перенос строк


В хинте могут отображаться данных в следующих формах:
1. Простой текст. Он должен "перетекать", т.е. если текст абзаца не помещается в шириру экрана, он (текст) должен быть автоматически перенесен на другую строку.
2. Табулированный текст. Его поведение я описал выше. Он должен быть в одну стоку если не превышает ширину экрана и таблично отформатированный и перенесен если не укладывается в одну строку.
3. Списки. Нечто вроде <ul><li>...</li><li>...</li>...</ul> в HTML. Каждый элемент отображается на отдельной строке и предваряется точкой — "•".
4. Список со скрываемыми, вложенными элементами. Это нужно для отображения сообщений об ошибках имеющих зависимые пункты. Скажем если у нас есть неоднозначность (несколько перегрузок), то каждая из перегрузок будет иметь ряд (от одного до нескольких) вложенных (зависимых) сообщений об ошибках в которых будет сказано почему данная перегрузка не подошла. Такие сообщения по умолчанию имеет смысл скрывать, а выводить только верхний уровень (в котором перечисляются только сообщения о том, какие перегрузки не прошли типизацию). Если пользователь захочет, он нажмет на плюск и увидит вложенные сообщения. В общем, это что-то вроде вложенного дерева элементы которого раскрываются (или схлопываются, если она уже были раскрыты) при нажатии.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.