/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/system.h" #include "common/unicode-bidi.h" #include "graphics/macgui/mactext.h" #include "gui/gui-manager.h" #include "gui/ThemeEngine.h" #include "gui/ThemeEval.h" #include "gui/widgets/richtext.h" #include "gui/widgets/scrollbar.h" namespace GUI { const Graphics::TTFMap ttfFamily[] = { {"NotoSans-Regular.ttf", Graphics::kMacFontRegular}, {"NotoSans-Bold.ttf", Graphics::kMacFontBold}, {"NotoSerif-Italic.ttf", Graphics::kMacFontItalic}, {"NotoSerif-Bold-Italic.ttf", Graphics::kMacFontBold | Graphics::kMacFontItalic}, {nullptr, 0} }; RichTextWidget::RichTextWidget(GuiObject *boss, int x, int y, int w, int h, bool scale, const Common::U32String &text, const Common::U32String &tooltip) : Widget(boss, x, y, w, h, scale, tooltip), CommandSender(nullptr) { _text = text; init(); } RichTextWidget::RichTextWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &text, const Common::U32String &tooltip) : RichTextWidget(boss, x, y, w, h, false, text, tooltip) { } RichTextWidget::RichTextWidget(GuiObject *boss, const Common::String &name, const Common::U32String &text, const Common::U32String &tooltip) : Widget(boss, name, tooltip), CommandSender(nullptr) { _text = text; init(); } void RichTextWidget::init() { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_TRACK_MOUSE | WIDGET_DYN_TOOLTIP); _type = kRichTextWidget; _verticalScroll = new ScrollBarWidget(this, _w - 16, 0, 16, _h); _verticalScroll->setTarget(this); _scrolledX = 0; _scrolledY = 0; _innerMargin = g_gui.xmlEval()->getVar("Globals.RichTextWidget.InnerMargin", 0); _scrollbarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0); _textWidth = MAX(1, _w - _scrollbarWidth - 2 * _innerMargin); _textHeight = MAX(1, _h - 2 * _innerMargin); _limitH = 140; } RichTextWidget::~RichTextWidget() { delete _txtWnd; if (_surface) _surface->free(); delete _surface; } void RichTextWidget::handleMouseWheel(int x, int y, int direction) { _verticalScroll->handleMouseWheel(x, y, direction); } void RichTextWidget::handleMouseDown(int x, int y, int button, int clickCount) { _mouseDownY = _mouseDownStartY = y; } void RichTextWidget::handleMouseUp(int x, int y, int button, int clickCount) { // Allow some tiny finger slipping if (ABS(_mouseDownY - _mouseDownStartY) > 5) { _mouseDownY = _mouseDownStartY = 0; return; } _mouseDownY = _mouseDownStartY = 0; Common::String link = _txtWnd->getMouseLink(x - _innerMargin + _scrolledX, y - _innerMargin + _scrolledY).encode(); if (link.hasPrefixIgnoreCase("http")) g_system->openUrl(link); } void RichTextWidget::handleMouseMoved(int x, int y, int button) { if (_mouseDownStartY == 0 || _mouseDownY == y) return; int h = _txtWnd->getTextHeight(); int prevScrolledY = _scrolledY; _scrolledY = CLIP(_scrolledY - (y - _mouseDownY), 0, h); _mouseDownY = y; if (_scrolledY == prevScrolledY) return; recalc(); _verticalScroll->recalc(); markAsDirty(); } void RichTextWidget::handleTooltipUpdate(int x, int y) { _tooltip = _txtWnd->getMouseLink(x - _innerMargin + _scrolledX, y - _innerMargin + _scrolledY); } void RichTextWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { Widget::handleCommand(sender, cmd, data); switch (cmd) { case kSetPositionCmd: _scrolledY = _verticalScroll->_currentPos; reflowLayout(); g_gui.scheduleTopDialogRedraw(); break; default: break; } } void RichTextWidget::recalc() { _innerMargin = g_gui.xmlEval()->getVar("Globals.RichTextWidget.InnerMargin", 0); _scrollbarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0); _textWidth = MAX(1, _w - _scrollbarWidth - 2 * _innerMargin); _textHeight = MAX(1, _h - 2 * _innerMargin); _limitH = _textHeight; // Workaround: Currently Graphics::MacText::setMaxWidth does not work well. // There is a known limitation that the size is skipped when the text contains table, // and there is also an issue with the font. // So for now we recreate the widget. // if (_txtWnd) { // _txtWnd->setMaxWidth(_textWidth); // if (_surface->w != _textWidth || _surface->h != _textHeight) // _surface->create(_textWidth, _textHeight, g_gui.getWM()->_pixelformat); // } else { // createWidget(); // } if (!_surface || _surface->w != _textWidth) { delete _txtWnd; createWidget(); } else if (_surface->h != _textHeight) _surface->create(_textWidth, _textHeight, g_gui.getWM()->_pixelformat); int h = _txtWnd->getTextHeight(); if (h <= _limitH) _scrolledY = 0; if (_scrolledY > h - _limitH) _scrolledY = MAX(0, h - _limitH); _verticalScroll->_numEntries = h; _verticalScroll->_currentPos = _scrolledY; _verticalScroll->_entriesPerPage = _limitH; _verticalScroll->_singleStep = _h / 4; _verticalScroll->setPos(_w - _scrollbarWidth, 0); _verticalScroll->setSize(_scrollbarWidth, _h - 1); } void RichTextWidget::createWidget() { Graphics::MacWindowManager *wm = g_gui.getWM(); uint32 bg = wm->_pixelformat.ARGBToColor(0, 0xff, 0xff, 0xff); // transparent TextColorData *normal = g_gui.theme()->getTextColorData(kTextColorNormal); uint32 fg = wm->_pixelformat.RGBToColor(normal->r, normal->g, normal->b); const int fontHeight = g_gui.xmlEval()->getVar("Globals.Font.Height", 25); #if 1 Graphics::MacFont macFont(Graphics::kMacFontNewYork, fontHeight, Graphics::kMacFontRegular); (void)ttfFamily; #else int newId = wm->_fontMan->registerTTFFont(ttfFamily); Graphics::MacFont macFont(newId, fontHeight, Graphics::kMacFontRegular); #endif _txtWnd = new Graphics::MacText(Common::U32String(), wm, &macFont, fg, bg, _textWidth, Graphics::kTextAlignLeft); if (!_imageArchive.empty()) _txtWnd->setImageArchive(_imageArchive); _txtWnd->setMarkdownText(_text); if (_surface) _surface->create(_textWidth, _textHeight, g_gui.getWM()->_pixelformat); else _surface = new Graphics::ManagedSurface(_textWidth, _textHeight, wm->_pixelformat); } void RichTextWidget::reflowLayout() { Widget::reflowLayout(); recalc(); _verticalScroll->setVisible(_verticalScroll->_numEntries > _limitH); //show when there is something to scroll _verticalScroll->recalc(); } void RichTextWidget::drawWidget() { if (!_txtWnd) recalc(); g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), ThemeEngine::kWidgetBackgroundPlain); _surface->clear(g_gui.getWM()->_pixelformat.ARGBToColor(0, 0xff, 0xff, 0xff)); // transparent _txtWnd->draw(_surface, 0, _scrolledY, _textWidth, _textHeight, 0, 0); g_gui.theme()->drawManagedSurface(Common::Point(_x + _innerMargin, _y + _innerMargin), *_surface, Graphics::ALPHA_FULL); } void RichTextWidget::draw() { Widget::draw(); if (_verticalScroll->isVisible()) { _verticalScroll->draw(); } } void RichTextWidget::markAsDirty() { Widget::markAsDirty(); if (_verticalScroll->isVisible()) { _verticalScroll->markAsDirty(); } } bool RichTextWidget::containsWidget(Widget *w) const { if (w == _verticalScroll || _verticalScroll->containsWidget(w)) return true; return false; } Widget *RichTextWidget::findWidget(int x, int y) { if (_verticalScroll->isVisible() && x >= _w - _scrollbarWidth) return _verticalScroll; return this; } } // End of namespace GUI