MythTV master
mythuitext.cpp
Go to the documentation of this file.
1
2#include "mythuitext.h"
3
4#include <algorithm>
5#include <cmath>
6
7#include <QCoreApplication>
8#include <QtGlobal>
9#include <QDomDocument>
10#include <QFontMetrics>
11#include <QString>
12#include <QHash>
13#include <QRegularExpression>
14
15#include "libmythbase/compat.h"
18
19#include "mythuihelper.h"
20#include "mythpainter.h"
21#include "mythmainwindow.h"
22#include "mythfontproperties.h"
23
24MythUIText::MythUIText(MythUIType *parent, const QString &name)
25 : MythUIType(parent, name),
26 m_font(new MythFontProperties())
27{
28 m_enableInitiator = true;
29
30 m_fontStates.insert("default", MythFontProperties());
31 *m_font = m_fontStates["default"];
32}
33
34MythUIText::MythUIText(const QString &text, const MythFontProperties &font,
35 QRect displayRect, QRect altDisplayRect,
36 MythUIType *parent, const QString &name)
37 : MythUIType(parent, name),
38 m_origDisplayRect(displayRect), m_altDisplayRect(altDisplayRect),
39 m_canvas(0, 0, displayRect.width(), displayRect.height()),
40 m_drawRect(displayRect),
41 m_message(text.trimmed()),
42 m_defaultMessage(text),
43 m_font(new MythFontProperties())
44{
45#if 0 // Not currently used
46 m_usingAltArea = false;
47#endif
48
49 m_enableInitiator = true;
50
51 MythUIText::SetArea(MythRect(displayRect));
52 m_fontStates.insert("default", font);
53 *m_font = m_fontStates["default"];
54}
55
57{
58 delete m_font;
59 m_font = nullptr;
60
61 for (auto *layout : std::as_const(m_layouts))
62 delete layout;
63}
64
66{
68 {
70 SetRedraw();
71 emit DependChanged(true);
72 }
73
74 SetFontState("default");
75
77}
78
80{
81 QString newText = GetTemplateText();
82
83 if (newText.isEmpty())
84 newText = GetDefaultText();
85
86 static const QRegularExpression re {R"(%(([^\|%]+)?\||\|(.))?([\w#]+)(\|(.+?))?%)",
87 QRegularExpression::DotMatchesEverythingOption};
88
89 bool replaced = map.contains(objectName());
90
91 if (!replaced && !newText.isEmpty() && newText.contains(re))
92 {
93 QString translatedTemplate = QCoreApplication::translate("ThemeUI",
94 newText.toUtf8().constData());
95
96 QRegularExpressionMatchIterator i = re.globalMatch(translatedTemplate);
97 while (i.hasNext()) {
98 QRegularExpressionMatch match = i.next();
99 QString key = match.captured(4).toLower().trimmed();
100
101 if (map.contains(key))
102 {
103 replaced = true;
104 break;
105 }
106 }
107 }
108
109 if (replaced)
110 {
111 Reset();
112 }
113}
114
115void MythUIText::SetText(const QString &text)
116{
117 const QString& newtext = text;
118
119 if (!m_layouts.isEmpty() && newtext == m_message)
120 return;
121
122 if (newtext.isEmpty())
123 {
125 emit DependChanged(true);
126 }
127 else
128 {
129 m_message = newtext;
130 emit DependChanged(false);
131 }
132 m_cutMessage.clear();
134
135 SetRedraw();
136}
137
139{
140 QString newText = GetTemplateText();
141
142 if (newText.isEmpty())
143 newText = GetDefaultText();
144
145 static const QRegularExpression re {R"(%(([^\|%]+)?\||\|(.))?([\w#]+)(\|(.+?))?%)",
146 QRegularExpression::DotMatchesEverythingOption};
147
148 if (!newText.isEmpty() && newText.contains(re))
149 {
150 QString translatedTemplate = QCoreApplication::translate("ThemeUI",
151 newText.toUtf8().constData());
152
153 QString tempString = translatedTemplate;
154 bool replaced = map.contains(objectName());
155
156 QRegularExpressionMatchIterator i = re.globalMatch(translatedTemplate);
157 while (i.hasNext()) {
158 QRegularExpressionMatch match = i.next();
159 QString key = match.captured(4).toLower().trimmed();
160 QString replacement;
161
162 if (map.contains(key))
163 {
164 replaced = true;
165 }
166 if (!map.value(key).isEmpty())
167 {
168 replacement = QString("%1%2%3%4")
169 .arg(match.captured(2),
170 match.captured(3),
171 map.value(key),
172 match.captured(6));
173 }
174
175 tempString.replace(match.captured(0), replacement);
176 }
177 if (replaced)
178 {
179 SetText(tempString);
180 }
181 }
182 else if (map.contains(objectName()))
183 {
184 SetText(map.value(objectName()));
185 }
186}
187
189{
190 m_fontStates.insert("default", fontProps);
191 if (m_font->GetHash() != m_fontStates["default"].GetHash())
192 {
193 *m_font = m_fontStates["default"];
194 if (!m_message.isEmpty())
195 {
197 SetRedraw();
198 }
199 }
200}
201
202void MythUIText::SetFontState(const QString &state)
203{
204 if (m_fontStates.contains(state))
205 {
206 if (m_font->GetHash() == m_fontStates[state].GetHash())
207 return;
208 *m_font = m_fontStates[state];
209 }
210 else
211 {
212 if (m_font->GetHash() == m_fontStates["default"].GetHash())
213 return;
214 *m_font = m_fontStates["default"];
215 }
216 if (!m_message.isEmpty())
217 {
219 SetRedraw();
220 }
221}
222
223#if 0 // Not currently used
224void MythUIText::UseAlternateArea(bool useAlt)
225{
226 if (useAlt && m_altDisplayRect.width() > 1)
227 {
229 m_usingAltArea = true;
230 }
231 else
232 {
234 m_usingAltArea = false;
235 }
236
238}
239#endif
240
242{
243 int h = just & Qt::AlignHorizontal_Mask;
244 int v = just & Qt::AlignVertical_Mask;
245
246 if ((h && (m_justification & Qt::AlignHorizontal_Mask) ^ h) ||
247 (v && (m_justification & Qt::AlignVertical_Mask) ^ v))
248 {
249 // preserve the wordbreak attribute, drop everything else
250 m_justification = m_justification & Qt::TextWordWrap;
251 m_justification |= just;
252 if (!m_message.isEmpty())
253 {
255 SetRedraw();
256 }
257 }
258}
259
261{
262 return m_justification;
263}
264
265void MythUIText::SetCutDown(Qt::TextElideMode mode)
266{
267 if (mode != m_cutdown)
268 {
269 m_cutdown = mode;
270 if (m_scrolling && m_cutdown != Qt::ElideNone)
271 {
272 LOG(VB_GENERAL, LOG_ERR, QString("'%1' (%2): <scroll> and "
273 "<cutdown> are not combinable.")
274 .arg(objectName(), GetXMLLocation()));
275 m_cutdown = Qt::ElideNone;
276 }
277 if (!m_message.isEmpty())
278 {
280 SetRedraw();
281 }
282 }
283}
284
285void MythUIText::SetMultiLine(bool multiline)
286{
287 if (multiline != m_multiLine)
288 {
289 m_multiLine = multiline;
290
291 if (m_multiLine)
292 m_justification |= Qt::TextWordWrap;
293 else
294 m_justification &= ~Qt::TextWordWrap;
295
296 if (!m_message.isEmpty())
297 {
299 SetRedraw();
300 }
301 }
302}
303
305{
307 m_cutMessage.clear();
308
311}
312
314{
317}
318
320{
321 QPoint newpoint(x, y);
322
323 if (newpoint == m_canvas.topLeft())
324 return;
325
326 m_canvas.moveTopLeft(newpoint);
327 SetRedraw();
328}
329
330void MythUIText::ShiftCanvas(int x, int y)
331{
332 if (x == 0 && y == 0)
333 return;
334
335 m_canvas.moveTop(m_canvas.y() + y);
336 m_canvas.moveLeft(m_canvas.x() + x);
337 SetRedraw();
338}
339
340void MythUIText::DrawSelf(MythPainter *p, int xoffset, int yoffset,
341 int alphaMod, QRect clipRect)
342{
343 if (m_canvas.isEmpty())
344 return;
345
347 QRect drawrect = m_drawRect.toQRect();
348 drawrect.translate(xoffset, yoffset);
349 QRect canvas = m_canvas.toQRect();
350
351 int alpha = CalcAlpha(alphaMod);
352
353 if (m_ascent)
354 {
355 drawrect.setY(drawrect.y() - m_ascent);
356 canvas.moveTop(canvas.y() + m_ascent);
357 canvas.setHeight(canvas.height() + m_ascent);
358 }
359 if (m_descent)
360 {
361 drawrect.setHeight(drawrect.height() + m_descent);
362 canvas.setHeight(canvas.height() + m_descent);
363 }
364
365 if (m_leftBearing)
366 {
367 drawrect.setX(drawrect.x() + m_leftBearing);
368 canvas.moveLeft(canvas.x() - m_leftBearing);
369 canvas.setWidth(canvas.width() - m_leftBearing);
370 }
371 if (m_rightBearing)
372 {
373 drawrect.setWidth(drawrect.width() - m_rightBearing);
374 canvas.setWidth(canvas.width() - m_rightBearing);
375 }
376
377 if (GetFontProperties()->hasOutline())
378 {
379 QColor outlineColor;
380 int outlineSize = 0;
381 int outlineAlpha = 255;
382
383 GetFontProperties()->GetOutline(outlineColor, outlineSize,
384 outlineAlpha);
385
386 MythPoint outline(outlineSize, outlineSize);
387
388 drawrect.setX(drawrect.x() - outline.x());
389 drawrect.setWidth(drawrect.width() + outline.x());
390 drawrect.setY(drawrect.y() - outline.y());
391 drawrect.setHeight(drawrect.height() + outline.y());
392
393 /* Canvas pos is where the view port (drawrect) pulls from, so
394 * it needs moved to the right for the left edge to be picked up*/
395 canvas.moveLeft(canvas.x() + outline.x());
396 canvas.setWidth(canvas.width() + outline.x());
397 canvas.moveTop(canvas.y() + outline.y());
398 canvas.setHeight(canvas.height() + outline.y());
399 }
400
401 if (GetFontProperties()->hasShadow())
402 {
403 QPoint shadowOffset;
404 QColor shadowColor;
405 int shadowAlpha = 255;
406
407 GetFontProperties()->GetShadow(shadowOffset, shadowColor, shadowAlpha);
408
409 MythPoint shadow(shadowOffset);
410 shadow.NormPoint(); // scale it to screen resolution
411
412 drawrect.setWidth(drawrect.width() + shadow.x());
413 drawrect.setHeight(drawrect.height() + shadow.y());
414
415 canvas.setWidth(canvas.width() + shadow.x());
416 canvas.setHeight(canvas.height() + shadow.y());
417 }
418
419 p->SetClipRect(clipRect);
420 p->DrawTextLayout(canvas, m_layouts, formats,
421 *GetFontProperties(), alpha, drawrect);
422}
423
424bool MythUIText::FormatTemplate(QString & paragraph, QTextLayout *layout)
425{
426 layout->clearFormats();
427
429 QTextLayout::FormatRange range;
430 QString fontname;
431 bool res = false; // Return true if paragraph changed.
432
433 if (GetFontProperties()->hasOutline())
434 {
435 int outlineSize = 0;
436 int outlineAlpha = 255;
437 QColor outlineColor;
438
439 GetFontProperties()->GetOutline(outlineColor, outlineSize,
440 outlineAlpha);
441
442 outlineColor.setAlpha(outlineAlpha);
443
444 MythPoint outline(outlineSize, outlineSize);
445 outline.NormPoint(); // scale it to screen resolution
446
447 QPen pen;
448 pen.setBrush(outlineColor);
449 pen.setWidth(outline.x());
450
451 range.start = 0;
452 range.length = paragraph.size();
453 range.format.setTextOutline(pen);
454 formats.push_back(range);
455 }
456
457 range.start = 0;
458 range.length = 0;
459
460 int pos = 0;
461 int end = 0;
462 while ((pos = paragraph.indexOf("[font]", pos, Qt::CaseInsensitive)) != -1)
463 {
464 end = paragraph.indexOf("[/font]", pos + 1, Qt::CaseInsensitive);
465 if (end != -1)
466 {
467 if (range.length == -1)
468 {
469 // End of the affected text
470 range.length = pos - range.start;
471 if (range.length > 0)
472 {
473 formats.push_back(range);
474 LOG(VB_GUI, LOG_DEBUG,
475 QString("'%1' Setting \"%2\" with FONT %3")
476 .arg(objectName(),
477 paragraph.mid(range.start, range.length),
478 fontname));
479 }
480 range.length = 0;
481 }
482
483 int len = end - pos - 6;
484 fontname = paragraph.mid(pos + 6, len);
485
486 if (GetGlobalFontMap()->Contains(fontname))
487 {
488 MythFontProperties *fnt = GetGlobalFontMap()->GetFont(fontname);
489 range.start = pos;
490 range.length = -1; // Need to find the end of the effect
491 range.format.setFont(fnt->face());
492 range.format.setFontStyleHint(QFont::SansSerif);
493 range.format.setForeground(fnt->GetBrush());
494 }
495 else
496 {
497 LOG(VB_GUI, LOG_ERR,
498 QString("'%1' Unknown Font '%2' specified in template.")
499 .arg(objectName(), fontname));
500 }
501
502 LOG(VB_GUI, LOG_DEBUG, QString("Removing %1 through %2 '%3'")
503 .arg(pos).arg(end + 7 - pos).arg(paragraph.mid(pos,
504 end + 7 - pos)));
505 paragraph.remove(pos, end + 7 - pos);
506 res = true;
507 }
508 else
509 {
510 LOG(VB_GUI, LOG_ERR,
511 QString("'%1' Non-terminated [font] found in template")
512 .arg(objectName()));
513 break;
514 }
515 }
516
517 if (range.length == -1) // To the end
518 {
519 range.length = paragraph.length() - range.start;
520 formats.push_back(range);
521 LOG(VB_GUI, LOG_DEBUG,
522 QString("'%1' Setting \"%2\" with FONT %3")
523 .arg(objectName(),
524 paragraph.mid(range.start, range.length),
525 fontname));
526 }
527
528 if (!formats.empty())
529 layout->setFormats(formats);
530
531 return res;
532}
533
534bool MythUIText::Layout(QString & paragraph, QTextLayout *layout, bool final,
535 bool & overflow, qreal width, qreal & height,
536 bool force, qreal & last_line_width,
537 QRectF & min_rect, int & num_lines)
538{
539 int last_line = 0;
540
541 FormatTemplate(paragraph, layout);
542 layout->setText(paragraph);
543 layout->beginLayout();
544 num_lines = 0;
545 for (;;)
546 {
547 QTextLine line = layout->createLine();
548 if (!line.isValid())
549 break;
550
551 // Try "visible" width first, so alignment works
552 line.setLineWidth(width);
553
554 if (!m_multiLine && line.textLength() < paragraph.size())
555 {
556 if (!force && m_cutdown != Qt::ElideNone)
557 {
558 QFontMetrics fm(GetFontProperties()->face());
559 paragraph = fm.elidedText(paragraph, m_cutdown,
560 width - fm.averageCharWidth());
561 return false;
562 }
563 // If text does not fit, then expand so canvas size is correct
564 line.setLineWidth(INT_MAX);
565 }
566
567 height += m_leading;
568 line.setPosition(QPointF(0, height));
569 height += m_lineHeight;
570 if (!overflow)
571 {
572 if (height > m_area.height())
573 {
574 LOG(VB_GUI, num_lines ? LOG_DEBUG : LOG_NOTICE,
575 QString("'%1' (%2): height overflow. line height %3 "
576 "paragraph height %4, area height %5")
577 .arg(objectName(),
579 .arg(line.height())
580 .arg(height)
581 .arg(m_area.height()));
582
583 if (!m_multiLine)
584 m_drawRect.setHeight(height);
585 if (m_cutdown != Qt::ElideNone)
586 {
587 QFontMetrics fm(GetFontProperties()->face());
588 QString cut_line = fm.elidedText
589 (paragraph.mid(last_line),
590 Qt::ElideRight,
591 width - fm.averageCharWidth());
592 paragraph = paragraph.left(last_line) + cut_line;
593 if (last_line == 0)
594 min_rect |= line.naturalTextRect();
595 return false;
596 }
597 overflow = true;
598 }
599 else
600 {
601 m_drawRect.setHeight(height);
602 }
603 if (!m_multiLine)
604 overflow = true;
605 }
606
607 last_line = line.textStart();
608 last_line_width = line.naturalTextWidth();
609 min_rect |= line.naturalTextRect();
610 ++num_lines;
611
612 if (final && line.textLength())
613 {
622 QFontMetrics fm(GetFontProperties()->face());
623
624 int bearing = fm.leftBearing(m_cutMessage[last_line]);
625 m_leftBearing = std::min(m_leftBearing, bearing);
626 bearing = fm.rightBearing
627 (m_cutMessage[last_line + line.textLength() - 1]);
628 m_rightBearing = std::min(m_rightBearing, bearing);
629 }
630 }
631
632 layout->endLayout();
633 return true;
634}
635
636bool MythUIText::LayoutParagraphs(const QStringList & paragraphs,
637 const QTextOption & textoption,
638 qreal width, qreal & height,
639 QRectF & min_rect, qreal & last_line_width,
640 int & num_lines, bool final)
641{
642 QStringList::const_iterator Ipara;
643 bool overflow = false;
644 int idx = 0;
645
646 for (auto *layout : std::as_const(m_layouts))
647 layout->clearLayout();
648
649 for (Ipara = paragraphs.begin(), idx = 0;
650 Ipara != paragraphs.end(); ++Ipara, ++idx)
651 {
652 QTextLayout *layout = m_layouts[idx];
653 layout->setTextOption(textoption);
654 layout->setFont(m_font->face());
655
656 QString para = *Ipara;
657 qreal saved_height = height;
658 QRectF saved_rect = min_rect;
659 if (!Layout(para, layout, final, overflow, width, height, false,
660 last_line_width, min_rect, num_lines))
661 {
662 // Again, with cut down
663 min_rect = saved_rect;
664 height = saved_height;
665 Layout(para, layout, final, overflow, width, height, true,
666 last_line_width, min_rect, num_lines);
667 break;
668 }
669 }
670 m_drawRect.setWidth(width);
671
672 return (!overflow);
673}
674
675bool MythUIText::GetNarrowWidth(const QStringList & paragraphs,
676 const QTextOption & textoption, qreal & width)
677{
678 qreal last_line_width = __builtin_nan("");
679 int last_width = -1;
680 int num_lines = 0;
681 Qt::TextElideMode cutdown = m_cutdown;
682 m_cutdown = Qt::ElideNone;
683
684 int line_height = m_leading + m_lineHeight;
685 width = m_area.width() / 2.0;
686 int best_width = m_area.width();
687 int too_narrow = 0;
688
689 for (int attempt = 0; attempt < 10; ++attempt)
690 {
691 QRectF min_rect;
692
694 qreal height = 0;
695
696 LayoutParagraphs(paragraphs, textoption, width, height,
697 min_rect, last_line_width, num_lines, false);
698
699 if (num_lines <= 0)
700 return false;
701
702 if (height > m_drawRect.height())
703 {
704 too_narrow = std::max<qreal>(too_narrow, width);
705
706 // Too narrow? How many lines didn't fit?
707 qreal lines = round((height - m_drawRect.height()) / line_height);
708 lines -= (1.0 - (last_line_width / width));
709 width += (lines * width) /
710 ((double)m_drawRect.height() / line_height);
711
712 if (width > best_width || static_cast<int>(width) == last_width)
713 {
714 width = best_width;
715 m_cutdown = cutdown;
716 return true;
717 }
718 }
719 else
720 {
721 best_width = std::min<qreal>(best_width, width);
722
723 qreal lines = floor((m_area.height() - height) / line_height);
724 if (lines >= 1)
725 {
726 // Too wide?
727 width -= width * (lines / num_lines - 1 + lines);
728 if (static_cast<int>(width) == last_width)
729 {
730 m_cutdown = cutdown;
731 return true;
732 }
733 }
734 else if (last_line_width < m_area.width())
735 {
736 // Is the last line fully used?
737 width -= (1.0 - last_line_width / width) / num_lines;
738 width = std::min(width, last_line_width);
739 if (static_cast<int>(width) == last_width)
740 {
741 m_cutdown = cutdown;
742 return true;
743 }
744 }
745 width = std::max<qreal>(width, too_narrow);
746 }
747 last_width = width;
748 }
749
750 LOG(VB_GENERAL, LOG_ERR, QString("'%1' (%2) GetNarrowWidth: Gave up "
751 "while trying to find optimal width "
752 "for '%3'.")
753 .arg(objectName(), GetXMLLocation(), m_cutMessage));
754
755 width = best_width;
756 m_cutdown = cutdown;
757 return false;
758}
759
761{
762 if (m_area.isNull())
763 return;
764
765 QRectF min_rect;
766 QFontMetrics fm(GetFontProperties()->face());
767
768 m_lineHeight = fm.height();
770 m_cutMessage.clear();
771 m_textCursor = -1;
772
774 {
775 bool isNumber = false;
776 int value = m_message.toInt(&isNumber);
777
778 if (isNumber && m_templateText.contains("%n"))
779 {
780 m_cutMessage = QCoreApplication::translate("ThemeUI",
781 m_templateText.toUtf8().constData(),
782 nullptr, qAbs(value));
783 }
784 else if (m_templateText.contains("%1"))
785 {
786 QString tmp = QCoreApplication::translate("ThemeUI",
787 m_templateText.toUtf8().constData());
789 }
790 }
791
792 if (m_cutMessage.isEmpty())
794
795 if (m_cutMessage.isEmpty())
796 {
797 if (m_layouts.empty())
798 m_layouts.push_back(new QTextLayout);
799
800 QTextLine line;
801 QTextOption textoption(static_cast<Qt::Alignment>(m_justification));
802 QVector<QTextLayout *>::iterator Ilayout = m_layouts.begin();
803
804 (*Ilayout)->setTextOption(textoption);
805 (*Ilayout)->setText("");
806 (*Ilayout)->beginLayout();
807 line = (*Ilayout)->createLine();
808 line.setLineWidth(m_area.width());
809 line.setPosition(QPointF(0, 0));
810 (*Ilayout)->endLayout();
811 m_drawRect.setWidth(m_area.width());
813
814 for (++Ilayout ; Ilayout != m_layouts.end(); ++Ilayout)
815 (*Ilayout)->clearLayout();
816
818 }
819 else
820 {
821 QStringList templist;
822 QStringList::iterator it;
823
824 switch (m_textCase)
825 {
826 case CaseUpper :
827 m_cutMessage = m_cutMessage.toUpper();
828 break;
829 case CaseLower :
830 m_cutMessage = m_cutMessage.toLower();
831 break;
833 //m_cutMessage = m_cutMessage.toLower();
834 templist = m_cutMessage.split(". ");
835
836 for (it = templist.begin(); it != templist.end(); ++it)
837 if (!(*it).isEmpty())
838 (*it).replace(0, 1, (*it).at(0).toUpper());
839
840 m_cutMessage = templist.join(". ");
841 break;
842 case CaseCapitaliseAll :
843 //m_cutMessage = m_cutMessage.toLower();
844 templist = m_cutMessage.split(" ");
845
846 for (it = templist.begin(); it != templist.end(); ++it)
847 if (!(*it).isEmpty())
848 (*it).replace(0, 1, (*it).at(0).toUpper());
849
850 m_cutMessage = templist.join(" ");
851 break;
852 case CaseNormal:
853 break;
854 }
855
856 QTextOption textoption(static_cast<Qt::Alignment>(m_justification));
857 textoption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
858
859 QStringList paragraphs = m_cutMessage.split('\n', Qt::KeepEmptyParts);
860 for (int idx = m_layouts.size(); idx < paragraphs.size(); ++idx)
861 m_layouts.push_back(new QTextLayout);
862
863 qreal width = __builtin_nan("");
865 m_minSize.isValid() && !m_cutMessage.isEmpty())
866 GetNarrowWidth(paragraphs, textoption, width);
867 else
868 width = m_area.width();
869
870 qreal height = 0;
872 int num_lines = 0;
873 qreal last_line_width = __builtin_nan("");
874 LayoutParagraphs(paragraphs, textoption, width, height,
875 min_rect, last_line_width, num_lines, true);
876
877 m_canvas.setRect(0, 0, min_rect.x() + min_rect.width(), height);
879 m_scrollBounce = false;
880
887 QRect actual = fm.boundingRect(m_cutMessage);
888 m_ascent = -(actual.y() + fm.ascent());
889 m_descent = actual.height() - fm.height();
890 }
891
892 if (m_scrolling)
893 {
897 {
898 if (m_canvas.width() > m_drawRect.width())
899 {
901 m_drawRect.setWidth(m_area.width());
903 }
904 }
905 else
906 {
907 if (m_canvas.height() > m_drawRect.height())
908 {
910 m_drawRect.setHeight(m_area.height());
912 }
913 }
914 }
915
916 // If any of hcenter|vcenter|Justify, center it all, then adjust
917 if ((m_justification & (Qt::AlignCenter|Qt::AlignJustify)) != 0U)
918 {
919 m_drawRect.moveCenter(m_area.center());
920 min_rect.moveCenter(m_area.center());
921 }
922
923 // Adjust horizontal
924 if (m_justification & Qt::AlignLeft)
925 {
926 // If text size is less than allowed min size, center it
927 if (m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
928 min_rect.width() < m_minSize.x())
929 {
931 (((m_minSize.x() - min_rect.width() +
932 fm.averageCharWidth()) / 2)));
933 min_rect.setWidth(m_minSize.x());
934 }
935 else
936 {
938 }
939
940 min_rect.moveLeft(m_area.x());
941 }
942 else if (m_justification & Qt::AlignRight)
943 {
944 // If text size is less than allowed min size, center it
945 if (m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
946 min_rect.width() < m_minSize.x())
947 {
948 m_drawRect.moveRight(m_area.x() + m_area.width() -
949 (((m_minSize.x() - min_rect.width() +
950 fm.averageCharWidth()) / 2)));
951 min_rect.setWidth(m_minSize.x());
952 }
953 else
954 {
955 m_drawRect.moveRight(m_area.x() + m_area.width());
956 }
957
958 min_rect.moveRight(m_area.x() + m_area.width());
959 }
960
961 // Adjust vertical
962 if (m_justification & Qt::AlignTop)
963 {
964 // If text size is less than allowed min size, center it
965 if (!m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
966 min_rect.height() < m_minSize.y())
967 {
969 ((m_minSize.y() - min_rect.height()) / 2));
970 min_rect.setHeight(m_minSize.y());
971 }
972 else
973 {
975 }
976
977 min_rect.moveTop(m_area.y());
978 }
979 else if (m_justification & Qt::AlignBottom)
980 {
981 // If text size is less than allowed min size, center it
982 if (!m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
983 min_rect.height() < m_minSize.y())
984 {
985 m_drawRect.moveBottom(m_area.y() + m_area.height() -
986 ((m_minSize.y() - min_rect.height()) / 2));
987 min_rect.setHeight(m_minSize.y());
988 }
989 else
990 {
991 m_drawRect.moveBottom(m_area.y() + m_area.height());
992 }
993
994 min_rect.moveBottom(m_area.y() + m_area.height());
995 }
996
998 if (m_minSize.isValid())
999 {
1000 // Record the minimal area needed for the message.
1001 SetMinArea(MythRect(min_rect.toRect()));
1002 }
1003}
1004
1006{
1007 int lineNo = -1;
1008 int lineCount = 0;
1009 int currPos = 0;
1010 int layoutStartPos = 0;
1011 int xPos = 0;
1012
1013 for (int x = 0; x < m_layouts.count(); x++)
1014 {
1015 QTextLayout *layout = m_layouts.at(x);
1016
1017 for (int y = 0; y < layout->lineCount(); y++)
1018 {
1019 lineCount++;
1020 if (lineNo != -1)
1021 continue;
1022
1023 QTextLine line = layout->lineAt(y);
1024
1025 if (m_textCursor >= currPos && m_textCursor < currPos + line.textLength()
1026 + (line.lineNumber() == layout->lineCount() - 1 ? 1 : 0))
1027 {
1028 lineNo = lineCount - 1;
1029 xPos = line.cursorToX(m_textCursor - layoutStartPos);
1030 continue;
1031 }
1032
1033 currPos += line.textLength();
1034 }
1035
1036 currPos += 1; // skip the newline
1037 layoutStartPos = currPos;
1038 }
1039
1040 // are we are at the top and need to scroll up
1041 if (lineNo == 0 && lines < 0)
1042 return -1;
1043
1044 // are we at the bottom and need to scroll down
1045 if (lineNo == lineCount - 1 && lines > 0)
1046 return -1;
1047
1048 if (lineNo == -1)
1049 {
1050 LOG(VB_GENERAL, LOG_ERR,
1051 QString("'%1' (%2) MoveCursor offset %3 not found in ANY paragraph!")
1052 .arg(objectName(), GetXMLLocation(), QString::number(m_textCursor)));
1053 return m_textCursor;
1054 }
1055
1056 int newLine = lineNo + lines;
1057
1058 newLine = std::max(newLine, 0);
1059
1060 if (newLine >= lineCount)
1061 newLine = lineCount - 1;
1062
1063 lineNo = -1;
1064 layoutStartPos = 0;
1065
1066 for (int x = 0; x < m_layouts.count(); x++)
1067 {
1068 QTextLayout *layout = m_layouts.at(x);
1069
1070 for (int y = 0; y < layout->lineCount(); y++)
1071 {
1072 lineNo++;
1073 QTextLine line = layout->lineAt(y);
1074
1075 if (lineNo == newLine)
1076 return layoutStartPos + line.xToCursor(xPos);
1077 }
1078
1079 layoutStartPos += layout->text().length() + 1;
1080 }
1081
1082 // should never reach here
1083 return m_textCursor;
1084}
1085
1086QPoint MythUIText::CursorPosition(int text_offset)
1087{
1088 if (m_layouts.empty())
1089 return m_area.topLeft().toQPoint();
1090
1091 if (text_offset == m_textCursor)
1092 return m_cursorPos;
1093 m_textCursor = text_offset;
1094
1095 QVector<QTextLayout *>::const_iterator Ipara {};
1096 QPoint pos;
1097 int x = 0;
1098 int y = 0;
1099 int offset = text_offset;
1100
1101 for (Ipara = m_layouts.constBegin(); Ipara != m_layouts.constEnd(); ++Ipara)
1102 {
1103 QTextLine line = (*Ipara)->lineForTextPosition(offset);
1104
1105 if (line.isValid())
1106 {
1107 pos.setX(line.cursorToX(&offset));
1108 pos.setY(line.y());
1109 break;
1110 }
1111 offset -= ((*Ipara)->text().size() + 1); // Account for \n
1112 }
1113 if (Ipara == m_layouts.constEnd())
1114 {
1115 LOG(VB_GENERAL, LOG_ERR,
1116 QString("'%1' (%2) CursorPosition offset %3 not found in "
1117 "ANY paragraph!")
1118 .arg(objectName(), GetXMLLocation(), QString::number(text_offset)));
1119 return m_area.topLeft().toQPoint();
1120 }
1121
1122 int mid = m_drawRect.width() / 2;
1123 if (m_canvas.width() <= m_drawRect.width() || pos.x() <= mid)
1124 x = 0; // start
1125 else if (pos.x() >= m_canvas.width() - mid) // end
1126 {
1127 x = m_canvas.width() - m_drawRect.width();
1128 pos.setX(pos.x() - x);
1129 }
1130 else // middle
1131 {
1132 x = pos.x() - mid;
1133 pos.setX(pos.x() - x);
1134 }
1135
1136 int line_height = m_lineHeight + m_leading;
1137 mid = m_area.height() / 2;
1138 mid -= (mid % line_height);
1139 y = pos.y() - mid;
1140
1141 if (y <= 0 || m_canvas.height() <= m_area.height()) // Top of buffer
1142 y = 0;
1143 else if (y + m_area.height() > m_canvas.height()) // Bottom of buffer
1144 {
1145 int visible_lines = ((m_area.height() / line_height) * line_height);
1146 y = m_canvas.height() - visible_lines;
1147 pos.setY(visible_lines - (m_canvas.height() - pos.y()));
1148 }
1149 else // somewhere in the middle
1150 {
1151 y -= m_leading;
1152 pos.setY(mid + m_leading);
1153 }
1154
1155 m_canvas.moveTopLeft(QPoint(-x, -y));
1156
1157 // Compensate for vertical alignment
1158 pos.setY(pos.y() + m_drawRect.y() - m_area.y());
1159
1160 pos += m_area.topLeft().toQPoint();
1161 m_cursorPos = pos;
1162
1163 return pos;
1164}
1165
1167{
1168 //
1169 // Call out base class pulse which will handle any alpha cycling and
1170 // movement
1171 //
1172
1174
1175 // Calculate interval since last update in msecs - clamped to 50msecs (20Hz)
1176 // in case of UI blocking/pauses
1177 int64_t currentmsecs = QDateTime::currentMSecsSinceEpoch();
1178 int64_t interval = std::min(currentmsecs - m_lastUpdate, static_cast<int64_t>(50));
1179 m_lastUpdate = currentmsecs;
1180
1181 // Rates are pixels per second (float) normalised to 70Hz for historical reasons.
1182 // Convert interval accordingly.
1183 float rate = (interval / 1000.0F) * DEFAULT_REFRESH_RATE;
1184
1185 if (m_colorCycling)
1186 {
1187 m_curR += m_incR;
1188 m_curG += m_incG;
1189 m_curB += m_incB;
1190
1191 m_curStep++;
1192
1193 if (m_curStep >= m_numSteps)
1194 {
1195 m_curStep = 0;
1196 m_incR *= -1;
1197 m_incG *= -1;
1198 m_incB *= -1;
1199 }
1200
1201 QColor newColor = QColor(static_cast<int>(m_curR),
1202 static_cast<int>(m_curG),
1203 static_cast<int>(m_curB));
1204 if (newColor != m_font->color())
1205 {
1206 m_font->SetColor(newColor);
1207 SetRedraw();
1208 }
1209 }
1210
1211 if (m_scrolling)
1212 {
1213 if (m_scrollPause > 0.0F)
1214 m_scrollPause -= rate;
1215 else
1216 {
1217 if (m_scrollBounce)
1219 else
1221 }
1222
1223 int whole = static_cast<int>(m_scrollPos);
1224 if (m_scrollPosWhole != whole)
1225 {
1226 int shift = whole - m_scrollPosWhole;
1227 m_scrollPosWhole = whole;
1228
1229 switch (m_scrollDirection)
1230 {
1231 case ScrollLeft :
1232 if (m_canvas.width() > m_drawRect.width())
1233 {
1234 ShiftCanvas(-shift, 0);
1235 if (m_canvas.x() + m_canvas.width() < 0)
1236 {
1237 SetCanvasPosition(m_drawRect.width(), 0);
1239 }
1240 }
1241 break;
1242 case ScrollRight :
1243 if (m_canvas.width() > m_drawRect.width())
1244 {
1245 ShiftCanvas(shift, 0);
1246 if (m_canvas.x() > m_drawRect.width())
1247 {
1248 SetCanvasPosition(-m_canvas.width(), 0);
1250 }
1251 }
1252 break;
1253 case ScrollHorizontal:
1254 if (m_canvas.width() <= m_drawRect.width())
1255 break;
1256 if (m_scrollBounce) // scroll right
1257 {
1258 if (m_canvas.x() + m_scrollOffset > m_drawRect.x())
1259 {
1260 m_scrollBounce = false;
1263 }
1264 else
1265 {
1266 ShiftCanvas(shift, 0);
1267 }
1268 }
1269 else // scroll left
1270 {
1271 if (m_canvas.x() + m_canvas.width() + m_scrollOffset <
1272 m_drawRect.x() + m_drawRect.width())
1273 {
1274 m_scrollBounce = true;
1277 }
1278 else
1279 {
1280 ShiftCanvas(-shift, 0);
1281 }
1282 }
1283 break;
1284 case ScrollUp :
1285 if (m_canvas.height() > m_drawRect.height())
1286 {
1287 ShiftCanvas(0, -shift);
1288 if (m_canvas.y() + m_canvas.height() < 0)
1289 {
1290 SetCanvasPosition(0, m_drawRect.height());
1292 }
1293 }
1294 break;
1295 case ScrollDown :
1296 if (m_canvas.height() > m_drawRect.height())
1297 {
1298 ShiftCanvas(0, shift);
1299 if (m_canvas.y() > m_drawRect.height())
1300 {
1301 SetCanvasPosition(0, -m_canvas.height());
1303 }
1304 }
1305 break;
1306 case ScrollVertical:
1307 if (m_canvas.height() <= m_drawRect.height())
1308 break;
1309 if (m_scrollBounce) // scroll down
1310 {
1311 if (m_canvas.y() + m_scrollOffset > m_drawRect.y())
1312 {
1313 m_scrollBounce = false;
1316 }
1317 else
1318 {
1319 ShiftCanvas(0, shift);
1320 }
1321 }
1322 else // scroll up
1323 {
1324 if (m_canvas.y() + m_canvas.height() + m_scrollOffset <
1325 m_drawRect.y() + m_drawRect.height())
1326 {
1327 m_scrollBounce = true;
1330 }
1331 else
1332 {
1333 ShiftCanvas(0, -shift);
1334 }
1335 }
1336 break;
1337 case ScrollNone:
1338 break;
1339 }
1340 }
1341 }
1342}
1343
1344void MythUIText::CycleColor(const QColor& startColor, const QColor& endColor, int numSteps)
1345{
1346 if (!GetPainter()->SupportsAnimation())
1347 return;
1348
1349 m_startColor = startColor;
1350 m_endColor = endColor;
1351 m_numSteps = numSteps;
1352 m_curStep = 0;
1353
1354 m_curR = startColor.red();
1355 m_curG = startColor.green();
1356 m_curB = startColor.blue();
1357
1358 m_incR = (endColor.red() * 1.0F - m_curR) / m_numSteps;
1359 m_incG = (endColor.green() * 1.0F - m_curG) / m_numSteps;
1360 m_incB = (endColor.blue() * 1.0F - m_curB) / m_numSteps;
1361
1362 m_colorCycling = true;
1363}
1364
1366{
1367 if (!m_colorCycling)
1368 return;
1369
1371 m_colorCycling = false;
1372 SetRedraw();
1373}
1374
1376 const QString &filename, QDomElement &element, bool showWarnings)
1377{
1378 if (element.tagName() == "area")
1379 {
1380 SetArea(parseRect(element));
1382 }
1383 // else if (element.tagName() == "altarea") // Unused, but maybe in future?
1384 // m_altDisplayRect = parseRect(element);
1385 else if (element.tagName() == "font")
1386 {
1387 QString fontname = getFirstText(element);
1388 MythFontProperties *fp = GetFont(fontname);
1389
1390 if (!fp)
1391 fp = GetGlobalFontMap()->GetFont(fontname);
1392
1393 if (fp)
1394 {
1395 MythFontProperties font = *fp;
1397 font.Rescale(window->GetUIScreenRect().height());
1398 font.AdjustStretch(window->GetFontStretch());
1399 QString state = element.attribute("state", "");
1400
1401 if (!state.isEmpty())
1402 {
1403 m_fontStates.insert(state, font);
1404 }
1405 else
1406 {
1407 m_fontStates.insert("default", font);
1408 *m_font = m_fontStates["default"];
1409 }
1410 }
1411 }
1412 else if (element.tagName() == "extraleading")
1413 {
1414 m_extraLeading = getFirstText(element).toInt();
1415 }
1416 else if (element.tagName() == "value")
1417 {
1418 if (element.attribute("lang", "").isEmpty())
1419 {
1420 m_message = QCoreApplication::translate("ThemeUI",
1421 parseText(element).toUtf8().constData());
1422 }
1423 else if ((element.attribute("lang", "").toLower() ==
1425 (element.attribute("lang", "").toLower() ==
1427 {
1428 m_message = parseText(element);
1429 }
1430
1433 }
1434 else if (element.tagName() == "template")
1435 {
1436 m_templateText = parseText(element);
1437 }
1438 else if (element.tagName() == "cutdown")
1439 {
1440 QString mode = getFirstText(element).toLower();
1441
1442 if (mode == "left")
1443 SetCutDown(Qt::ElideLeft);
1444 else if (mode == "middle")
1445 SetCutDown(Qt::ElideMiddle);
1446 else if (mode == "right" || parseBool(element))
1447 SetCutDown(Qt::ElideRight);
1448 else
1449 SetCutDown(Qt::ElideNone);
1450 }
1451 else if (element.tagName() == "multiline")
1452 {
1453 SetMultiLine(parseBool(element));
1454 }
1455 else if (element.tagName() == "align")
1456 {
1457 QString align = getFirstText(element).toLower();
1459 }
1460 else if (element.tagName() == "colorcycle")
1461 {
1462 if (GetPainter()->SupportsAnimation())
1463 {
1464 QString tmp = element.attribute("start");
1465
1466 if (!tmp.isEmpty())
1467 m_startColor = QColor(tmp);
1468
1469 tmp = element.attribute("end");
1470
1471 if (!tmp.isEmpty())
1472 m_endColor = QColor(tmp);
1473
1474 tmp = element.attribute("steps");
1475
1476 if (!tmp.isEmpty())
1477 m_numSteps = tmp.toInt();
1478
1479 // initialize the rest of the stuff
1481 }
1482 else
1483 {
1484 m_colorCycling = false;
1485 }
1486
1487 m_colorCycling = parseBool(element.attribute("disable"));
1488 }
1489 else if (element.tagName() == "scroll")
1490 {
1491 if (GetPainter()->SupportsAnimation())
1492 {
1493 QString tmp = element.attribute("direction");
1494
1495 if (!tmp.isEmpty())
1496 {
1497 tmp = tmp.toLower();
1498
1499 if (tmp == "left")
1501 else if (tmp == "right")
1503 else if (tmp == "up")
1505 else if (tmp == "down")
1507 else if (tmp == "horizontal")
1509 else if (tmp == "vertical")
1511 else
1512 {
1514 LOG(VB_GENERAL, LOG_ERR,
1515 QString("'%1' (%2) Invalid scroll attribute")
1516 .arg(objectName(), GetXMLLocation()));
1517 }
1518 }
1519
1520 tmp = element.attribute("startdelay");
1521 if (!tmp.isEmpty())
1522 {
1523 float seconds = tmp.toFloat();
1524 m_scrollStartDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1525 }
1526 tmp = element.attribute("returndelay");
1527 if (!tmp.isEmpty())
1528 {
1529 float seconds = tmp.toFloat();
1530 m_scrollReturnDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1531 }
1532 tmp = element.attribute("rate");
1533 if (!tmp.isEmpty())
1534 {
1535#if 0 // scroll rate as a percentage of 70Hz
1536 float percent = tmp.toFloat() / 100.0;
1537 m_scrollForwardRate = percent;
1538#else // scroll rate as pixels per second
1539 int pixels = tmp.toInt();
1540 m_scrollForwardRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1541#endif
1542 }
1543 tmp = element.attribute("returnrate");
1544 if (!tmp.isEmpty())
1545 {
1546#if 0 // scroll rate as a percentage of 70Hz
1547 float percent = tmp.toFloat() / 100.0;
1548 m_scrollReturnRate = percent;
1549#else // scroll rate as pixels per second
1550 int pixels = tmp.toInt();
1551 m_scrollReturnRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1552#endif
1553 }
1554
1555 m_scrolling = true;
1556 }
1557 else
1558 {
1559 m_scrolling = false;
1560 }
1561 }
1562 else if (element.tagName() == "case")
1563 {
1564 QString stringCase = getFirstText(element).toLower();
1565
1566 if (stringCase == "lower")
1568 else if (stringCase == "upper")
1570 else if (stringCase == "capitalisefirst")
1572 else if (stringCase == "capitaliseall")
1574 else
1576 }
1577 else
1578 {
1579 if (element.tagName() == "minsize" && element.hasAttribute("shrink"))
1580 {
1581 m_shrinkNarrow = (element.attribute("shrink")
1582 .toLower() != "short");
1583 }
1584
1585 return MythUIType::ParseElement(filename, element, showWarnings);
1586 }
1587
1588 return true;
1589}
1590
1592{
1593 auto *text = dynamic_cast<MythUIText *>(base);
1594
1595 if (!text)
1596 {
1597 LOG(VB_GENERAL, LOG_ERR,
1598 QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1599 .arg(objectName(), GetXMLLocation(),
1600 base->objectName(), base->GetXMLLocation()));
1601 return;
1602 }
1603
1604 m_justification = text->m_justification;
1605 m_origDisplayRect = text->m_origDisplayRect;
1606 m_altDisplayRect = text->m_altDisplayRect;
1607 m_canvas = text->m_canvas;
1608 m_drawRect = text->m_drawRect;
1609
1610 m_defaultMessage = text->m_defaultMessage;
1611 SetText(text->m_message);
1612 m_cutMessage = text->m_cutMessage;
1613 m_templateText = text->m_templateText;
1614
1615 m_shrinkNarrow = text->m_shrinkNarrow;
1616 m_cutdown = text->m_cutdown;
1617 m_multiLine = text->m_multiLine;
1618 m_leading = text->m_leading;
1619 m_extraLeading = text->m_extraLeading;
1620 m_lineHeight = text->m_lineHeight;
1621 m_textCursor = text->m_textCursor;
1622
1623 for (auto it = text->m_fontStates.cbegin(); it != text->m_fontStates.cend(); ++it)
1624 m_fontStates.insert(it.key(), it.value());
1625
1626 *m_font = m_fontStates["default"];
1627
1628 m_colorCycling = text->m_colorCycling;
1629 m_startColor = text->m_startColor;
1630 m_endColor = text->m_endColor;
1631 m_numSteps = text->m_numSteps;
1632 m_curStep = text->m_curStep;
1633 m_curR = text->m_curR;
1634 m_curG = text->m_curG;
1635 m_curB = text->m_curB;
1636 m_incR = text->m_incR;
1637 m_incG = text->m_incG;
1638 m_incB = text->m_incB;
1639
1640 m_scrollStartDelay = text->m_scrollStartDelay;
1641 m_scrollReturnDelay = text->m_scrollReturnDelay;
1642 m_scrollForwardRate = text->m_scrollForwardRate;
1643 m_scrollReturnRate = text->m_scrollReturnRate;
1644 m_scrollDirection = text->m_scrollDirection;
1645 m_scrolling = text->m_scrolling;
1646
1647 m_textCase = text->m_textCase;
1648
1651}
1652
1654{
1655 auto *text = new MythUIText(parent, objectName());
1656 text->CopyFrom(this);
1657}
1658
1659
1661{
1662 if (m_scrolling && m_cutdown != Qt::ElideNone)
1663 {
1664 LOG(VB_GENERAL, LOG_ERR,
1665 QString("'%1' (%2): <scroll> and <cutdown> are not combinable.")
1666 .arg(objectName(), GetXMLLocation()));
1667 m_cutdown = Qt::ElideNone;
1668 }
1670}
static const std::array< const std::string, 8 > formats
MythFontProperties * GetFont(const QString &text)
QString GetLanguage(void)
Returns two character ISO-639 language descriptor for UI language.
QString GetLanguageAndVariant(void)
Returns the user-set language and variant.
void AdjustStretch(int stretch)
QBrush GetBrush(void) const
QColor color(void) const
void GetShadow(QPoint &offset, QColor &color, int &alpha) const
void GetOutline(QColor &color, int &size, int &alpha) const
QFont face(void) const
void SetColor(const QColor &color)
QString GetHash(void) const
Wrapper around QPoint allowing us to handle percentage and other relative values for positioning in m...
Definition: mythrect.h:89
bool isValid(void) const
Definition: mythrect.h:102
void NormPoint(void)
Definition: mythrect.cpp:471
QPoint toQPoint(void) const
Definition: mythrect.cpp:594
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:18
void setRect(const QString &sX, const QString &sY, const QString &sWidth, const QString &sHeight, const QString &baseRes=QString())
Definition: mythrect.cpp:139
MythPoint topLeft(void) const
Definition: mythrect.cpp:288
void setY(const QString &sY)
Definition: mythrect.cpp:256
void moveTop(const QString &sY)
Definition: mythrect.cpp:319
void moveLeft(const QString &sX)
Definition: mythrect.cpp:309
void setX(const QString &sX)
Definition: mythrect.cpp:246
void setWidth(const QString &sWidth)
Definition: mythrect.cpp:266
void setHeight(const QString &sHeight)
Definition: mythrect.cpp:277
void moveTopLeft(QPoint point)
Definition: mythrect.cpp:296
QRect toQRect(void) const
Definition: mythrect.cpp:405
All purpose text widget, displays a text string.
Definition: mythuitext.h:29
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
int m_numSteps
Definition: mythuitext.h:144
QString m_templateText
Definition: mythuitext.h:119
MythUIText(MythUIType *parent, const QString &name)
Definition: mythuitext.cpp:24
float m_incR
Definition: mythuitext.h:149
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
Definition: mythuitext.cpp:340
int m_descent
Definition: mythuitext.h:128
float m_scrollForwardRate
Definition: mythuitext.h:163
bool m_colorCycling
Definition: mythuitext.h:141
@ CaseCapitaliseFirst
Definition: mythuitext.h:174
@ CaseCapitaliseAll
Definition: mythuitext.h:175
@ ScrollVertical
Definition: mythuitext.h:158
@ ScrollHorizontal
Definition: mythuitext.h:158
bool m_scrolling
Definition: mythuitext.h:170
bool LayoutParagraphs(const QStringList &paragraphs, const QTextOption &textoption, qreal width, qreal &height, QRectF &min_rect, qreal &last_line_width, int &num_lines, bool final)
Definition: mythuitext.cpp:636
bool m_scrollBounce
Definition: mythuitext.h:165
float m_incB
Definition: mythuitext.h:151
void SetArea(const MythRect &rect) override
Definition: mythuitext.cpp:304
int m_scrollPosWhole
Definition: mythuitext.h:168
void SetPosition(const MythPoint &pos) override
Definition: mythuitext.cpp:313
Qt::TextElideMode m_cutdown
Definition: mythuitext.h:125
~MythUIText() override
Definition: mythuitext.cpp:56
int MoveCursor(int lines)
int m_leading
Definition: mythuitext.h:131
MythFontProperties * m_font
Definition: mythuitext.h:138
float m_scrollReturnRate
Definition: mythuitext.h:164
FontStates m_fontStates
Definition: mythuitext.h:139
void ShiftCanvas(int x, int y)
Definition: mythuitext.cpp:330
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
QString m_message
Definition: mythuitext.h:116
bool FormatTemplate(QString &paragraph, QTextLayout *layout)
Definition: mythuitext.cpp:424
MythRect m_drawRect
Definition: mythuitext.h:113
QPoint CursorPosition(int text_offset)
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:65
void CycleColor(const QColor &startColor, const QColor &endColor, int numSteps)
void SetJustification(int just)
Definition: mythuitext.cpp:241
float m_curR
Definition: mythuitext.h:146
void SetMultiLine(bool multiline)
Definition: mythuitext.cpp:285
float m_curG
Definition: mythuitext.h:147
QString m_defaultMessage
Definition: mythuitext.h:118
MythRect m_altDisplayRect
Definition: mythuitext.h:111
void StopCycling()
QString GetTemplateText(void) const
Definition: mythuitext.h:49
void SetFontProperties(const MythFontProperties &fontProps)
Definition: mythuitext.cpp:188
QColor m_startColor
Definition: mythuitext.h:142
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
void SetTextFromMap(const InfoMap &map)
Definition: mythuitext.cpp:138
QColor m_endColor
Definition: mythuitext.h:143
int GetJustification(void) const
Definition: mythuitext.cpp:260
int m_leftBearing
Definition: mythuitext.h:129
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
void ResetMap(const InfoMap &map)
Definition: mythuitext.cpp:79
float m_scrollPause
Definition: mythuitext.h:162
float m_incG
Definition: mythuitext.h:150
int m_ascent
Definition: mythuitext.h:127
bool Layout(QString &paragraph, QTextLayout *layout, bool final, bool &overflow, qreal width, qreal &height, bool force, qreal &last_line_width, QRectF &min_rect, int &num_lines)
Definition: mythuitext.cpp:534
int m_extraLeading
Definition: mythuitext.h:132
MythRect m_origDisplayRect
Definition: mythuitext.h:110
int m_scrollOffset
Definition: mythuitext.h:166
bool m_shrinkNarrow
Definition: mythuitext.h:124
QPoint m_cursorPos
Definition: mythuitext.h:114
void FillCutMessage(void)
Definition: mythuitext.cpp:760
int m_curStep
Definition: mythuitext.h:145
MythRect m_canvas
Definition: mythuitext.h:112
int m_textCursor
Definition: mythuitext.h:134
QString m_cutMessage
Definition: mythuitext.h:117
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
int64_t m_lastUpdate
Definition: mythuitext.h:171
const MythFontProperties * GetFontProperties()
Definition: mythuitext.h:79
int m_scrollStartDelay
Definition: mythuitext.h:160
int m_scrollReturnDelay
Definition: mythuitext.h:161
float m_scrollPos
Definition: mythuitext.h:167
bool GetNarrowWidth(const QStringList &paragraphs, const QTextOption &textoption, qreal &width)
Definition: mythuitext.cpp:675
int m_justification
Definition: mythuitext.h:109
TextCase m_textCase
Definition: mythuitext.h:177
void SetFontState(const QString &state)
Definition: mythuitext.cpp:202
bool m_multiLine
Definition: mythuitext.h:126
int m_lineHeight
Definition: mythuitext.h:133
QString GetDefaultText(void) const
Definition: mythuitext.h:44
void SetCanvasPosition(int x, int y)
Definition: mythuitext.cpp:319
ScrollDir m_scrollDirection
Definition: mythuitext.h:169
void SetCutDown(Qt::TextElideMode mode)
Definition: mythuitext.cpp:265
QVector< QTextLayout * > m_layouts
Definition: mythuitext.h:136
float m_curB
Definition: mythuitext.h:148
int m_rightBearing
Definition: mythuitext.h:130
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
The base class on which all widgets and screens are based.
Definition: mythuitype.h:86
bool m_enableInitiator
Definition: mythuitype.h:267
bool m_initiator
Definition: mythuitype.h:268
virtual MythPainter * GetPainter(void)
QString GetXMLLocation(void) const
Definition: mythuitype.h:182
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:595
MythPoint m_minSize
Definition: mythuitype.h:279
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
void SetRedraw(void)
Definition: mythuitype.cpp:298
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:805
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:441
MythFontProperties * GetFont(const QString &text) const
int CalcAlpha(int alphamod) const
Definition: mythuitype.cpp:459
void SetPosition(int x, int y)
Convenience method, calls SetPosition(const MythPoint&) Override that instead to change functionality...
Definition: mythuitype.cpp:518
void DependChanged(bool isDefault)
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitype.cpp:70
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
MythRect m_area
Definition: mythuitype.h:277
static MythRect parseRect(const QString &text, bool normalize=true)
static int parseAlignment(const QString &text)
static QString getFirstText(QDomElement &element)
static bool parseBool(const QString &text)
static QString parseText(QDomElement &element)
static uint32_t * tmp
Definition: goom_core.cpp:28
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
FontMap * GetGlobalFontMap(void)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
QVector< QTextLayout::FormatRange > FormatVector
Definition: mythpainter.h:28
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
static constexpr uint8_t DEFAULT_REFRESH_RATE
Definition: mythuitext.h:14