Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, <Аноним>, Вы писали:
А>>У меня парочка мыслей появилась после прочтения статьи и беглово знакомства с исходниками:
А>>1. Зачем делать word wrap для строк которые не видны на экране? Лишняя работа.
VD>Чтобы сделать точный скролинг и точное отображение позиции в документе.
Вернее чтобы упростить реализацию этих функций
А>>2. Для адресации позиции в тексте достаточно знать смещение символа от начала текста, а уж строку которой принадлежит символ и смещение в строке можно расчитать.
VD>И каждый раз бегать по тексту рассчитывая строку? А смысл? Тормоза от этого конечно увеличатся, а вот бенефиты...
Если юзать string[] для хранения текста, то да, смысла нет, не спорю.
...
VD>Как-то проблем с масштабируемостью я пока не видел. Как и ее необходимости. Поясни, плиз, о чем речь.
Загрузили в редактор большой текст и стали его с головы редактировать. Ограничения стурктуры данных (в данном случае string[]) не будут видны пока мощности железа хватает, но рано или поздно они вылезут. Вообще IMHO использование Position<Document> для адресации сомволов очень напоминает старничную память с кошмариками в виде long и short pointer-ов.
...
VD>1. Так плохой идеей было использовать дотнетный Font. Это не позволяет менять в стилях отдельные атрибуты шрифта. Например, чтобы сделать жирным ключевые слова. Это же не дает реализовать простое масштабирование шрифтов. Мне очень нравится как в Сцинтиле или ИЕ можно менять размер ширфтов просто покрутив колесико мыши удерживая при этом Ctrl.
Думаю это не проблема шрифта, а не совсем удачный дизайн класса Style. Если пользователи захотят что-то большее кроме как рисовать вместо текста картинки? Например обводить подстроку в рамку. Что делать будете? Добавлять поле _drawRectAroundText в Style? IMHO коряво.
Я сам счас редактор делаю. Вот как раскраска у меня задизайнена:
class TextStyle
{
public:
COLORREF fgColor;
COLORREF bgColor;
HFONT font;
public:
TextStyle(): fgColor(CLR_INVALID), bgColor(CLR_INVALID), font(0) {}
};
class IDecoratorListener;
class IDecoratedSubstring;
class IDecorator
{
public:
virtual ~IDecorator() {}
public:
virtual void
decorate(
unsigned line,
const char_type* str,
unsigned len,
IDecoratorListener& dl) const = 0;
};
class IDecoratorListener
{
public:
virtual ~IDecoratorListener() {}
public:
virtual bool
onSubstringDecorated(
unsigned line,
const char_type* str,
unsigned strLen,
const char_type* substr,
unsigned substrLen,
const TextStyle& ts) = 0;
virtual bool
onSubstringDecorated(
unsigned line,
const char_type* str,
unsigned strLen,
const char_type* substr,
unsigned substrLen,
const IDecoratedSubstring& ds) = 0;
};
class IDecoratedSubstring
{
public:
virtual ~IDecoratedSubstring() {}
public:
virtual unsigned
getWidth(unsigned width) const = 0;
virtual unsigned
getHeight(unsigned height) const = 0;
virtual FONT
getFont() const = 0;
virtual void
paint(HDC g, unsigned width, unsigned height) const = 0;
};
Вот, например, как можно сделать изменение размера шрифта "как в Сцинтиле или ИЕ":
class FontChangeDecorator : public IDecorator, IDecoratorListener
{
IDecorator* decorator_;
IDecoratorListener* listener_;
public:
FontChangeDecorator(IDecorator* decorator): decorator_(decorator) {}
public:
virtual void
decorate(
unsigned line,
const char_type* str,
unsigned len,
IDecoratorListener& dl) const
{
// запомнить текущее значение listener_, чтобы восстановить его в случае рекурсивного
// вызова FontChangeDecorator::decorate
IDecoratorListener* l = listener_;
// подменить листенер
listener_ = dl;
decorator_->decorate(line, str, len, *this);
// восстановить листенер
listener_ = l;
}
private:
virtual bool
onSubstringDecorated(
unsigned line,
const char_type* str,
unsigned strLen,
const char_type* substr,
unsigned substrLen,
const TextStyle& ts)
{
HDC dc = getTempDC(); // каким-то образом получить HDC
::SelectFont(dc, ts.font);
TEXTMETRICS tm;
::GetTextMetrics(dc, &tm);
LOGFONT lf = textMetrics2LogFont(tm); // как-то сконвертировали
lf.height += (lf.height * 10) / 100; // увеличить шрифт на 10%
TextStyle newTs(ts);
newTs.font = ::CreateFontIndirect(lf);
// передать в листенер измененный шрифт
listener_.onSubstringDecorated(line, str, strLen, substr, substrLen, newTs);
}
};
Использование FontChangeDecorator:
void
setFontChangeDecorator(HWND hwnd /* хендл на окно моего редактора */)
{
IDecorator* d = ::SendMessage(hwnd, MYEDITOR_MSG_GETDECORATOR, 0, 0);
IDecorator* d2 = new FontChangeDecorator(d);
::SendMessage(hwnd, MYEDITOR_MSG_SETDECORATOR, d2, 0)
}
Редактор дергает IDecorator::decorate каждый раз когда надо отрисовать строку текста передавая свои реализации интерфейса IDecoratorListener. Есть реализации IDecoratorListener-а которые высчитывают ширину подстроки/целой строки, высоту ну и т.д. Word wrap так же сделан через IDecoratorListener.
Бенефиты такого подхода:
1. декоратор дергается только для тех строк которые видны на экране — не надо мучать процессор лишней работой;
2. легко сделать кэш строк обработанных декоратором, т.к. для этого достаточно просто написать еще одну реализацию IDecoratorListener;
3. Визуализация текста не ограничена рисованием текста или рисованием картинки вместо текста, т.к. реализация IDecoratedSubstring ограничена только фантазией юзверя (в разумных пределах конечно

;
4. декораторы можно комбинировать. Пример — FontChangeDecorator который изменяет результаты разбора уже существующего декоратора.
Алексей