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());
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());
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(), nullptr,
782 qAbs(value));
783 }
784 else if (m_templateText.contains("%1"))
785 {
786 QString tmp = QCoreApplication::translate("ThemeUI", m_templateText.toUtf8());
788 }
789 }
790
791 if (m_cutMessage.isEmpty())
793
794 if (m_cutMessage.isEmpty())
795 {
796 if (m_layouts.empty())
797 m_layouts.push_back(new QTextLayout);
798
799 QTextLine line;
800 QTextOption textoption(static_cast<Qt::Alignment>(m_justification));
801 QVector<QTextLayout *>::iterator Ilayout = m_layouts.begin();
802
803 (*Ilayout)->setTextOption(textoption);
804 (*Ilayout)->setText("");
805 (*Ilayout)->beginLayout();
806 line = (*Ilayout)->createLine();
807 line.setLineWidth(m_area.width());
808 line.setPosition(QPointF(0, 0));
809 (*Ilayout)->endLayout();
810 m_drawRect.setWidth(m_area.width());
812
813 for (++Ilayout ; Ilayout != m_layouts.end(); ++Ilayout)
814 (*Ilayout)->clearLayout();
815
817 }
818 else
819 {
820 QStringList templist;
821 QStringList::iterator it;
822
823 switch (m_textCase)
824 {
825 case CaseUpper :
826 m_cutMessage = m_cutMessage.toUpper();
827 break;
828 case CaseLower :
829 m_cutMessage = m_cutMessage.toLower();
830 break;
832 //m_cutMessage = m_cutMessage.toLower();
833 templist = m_cutMessage.split(". ");
834
835 for (it = templist.begin(); it != templist.end(); ++it)
836 if (!(*it).isEmpty())
837 (*it).replace(0, 1, (*it).at(0).toUpper());
838
839 m_cutMessage = templist.join(". ");
840 break;
841 case CaseCapitaliseAll :
842 //m_cutMessage = m_cutMessage.toLower();
843 templist = m_cutMessage.split(" ");
844
845 for (it = templist.begin(); it != templist.end(); ++it)
846 if (!(*it).isEmpty())
847 (*it).replace(0, 1, (*it).at(0).toUpper());
848
849 m_cutMessage = templist.join(" ");
850 break;
851 case CaseNormal:
852 break;
853 }
854
855 QTextOption textoption(static_cast<Qt::Alignment>(m_justification));
856 textoption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
857
858 QStringList paragraphs = m_cutMessage.split('\n', Qt::KeepEmptyParts);
859 for (int idx = m_layouts.size(); idx < paragraphs.size(); ++idx)
860 m_layouts.push_back(new QTextLayout);
861
862 qreal width = __builtin_nan("");
864 m_minSize.isValid() && !m_cutMessage.isEmpty())
865 GetNarrowWidth(paragraphs, textoption, width);
866 else
867 width = m_area.width();
868
869 qreal height = 0;
871 int num_lines = 0;
872 qreal last_line_width = __builtin_nan("");
873 LayoutParagraphs(paragraphs, textoption, width, height,
874 min_rect, last_line_width, num_lines, true);
875
876 m_canvas.setRect(0, 0, min_rect.x() + min_rect.width(), height);
878 m_scrollBounce = false;
879
886 QRect actual = fm.boundingRect(m_cutMessage);
887 m_ascent = -(actual.y() + fm.ascent());
888 m_descent = actual.height() - fm.height();
889 }
890
891 if (m_scrolling)
892 {
896 {
897 if (m_canvas.width() > m_drawRect.width())
898 {
900 m_drawRect.setWidth(m_area.width());
902 }
903 }
904 else
905 {
906 if (m_canvas.height() > m_drawRect.height())
907 {
909 m_drawRect.setHeight(m_area.height());
911 }
912 }
913 }
914
915 // If any of hcenter|vcenter|Justify, center it all, then adjust
916 if ((m_justification & (Qt::AlignCenter|Qt::AlignJustify)) != 0U)
917 {
918 m_drawRect.moveCenter(m_area.center());
919 min_rect.moveCenter(m_area.center());
920 }
921
922 // Adjust horizontal
923 if (m_justification & Qt::AlignLeft)
924 {
925 // If text size is less than allowed min size, center it
926 if (m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
927 min_rect.width() < m_minSize.x())
928 {
930 (((m_minSize.x() - min_rect.width() +
931 fm.averageCharWidth()) / 2)));
932 min_rect.setWidth(m_minSize.x());
933 }
934 else
935 {
937 }
938
939 min_rect.moveLeft(m_area.x());
940 }
941 else if (m_justification & Qt::AlignRight)
942 {
943 // If text size is less than allowed min size, center it
944 if (m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
945 min_rect.width() < m_minSize.x())
946 {
947 m_drawRect.moveRight(m_area.x() + m_area.width() -
948 (((m_minSize.x() - min_rect.width() +
949 fm.averageCharWidth()) / 2)));
950 min_rect.setWidth(m_minSize.x());
951 }
952 else
953 {
954 m_drawRect.moveRight(m_area.x() + m_area.width());
955 }
956
957 min_rect.moveRight(m_area.x() + m_area.width());
958 }
959
960 // Adjust vertical
961 if (m_justification & Qt::AlignTop)
962 {
963 // If text size is less than allowed min size, center it
964 if (!m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
965 min_rect.height() < m_minSize.y())
966 {
968 ((m_minSize.y() - min_rect.height()) / 2));
969 min_rect.setHeight(m_minSize.y());
970 }
971 else
972 {
974 }
975
976 min_rect.moveTop(m_area.y());
977 }
978 else if (m_justification & Qt::AlignBottom)
979 {
980 // If text size is less than allowed min size, center it
981 if (!m_shrinkNarrow && m_minSize.isValid() && min_rect.isValid() &&
982 min_rect.height() < m_minSize.y())
983 {
984 m_drawRect.moveBottom(m_area.y() + m_area.height() -
985 ((m_minSize.y() - min_rect.height()) / 2));
986 min_rect.setHeight(m_minSize.y());
987 }
988 else
989 {
990 m_drawRect.moveBottom(m_area.y() + m_area.height());
991 }
992
993 min_rect.moveBottom(m_area.y() + m_area.height());
994 }
995
997 if (m_minSize.isValid())
998 {
999 // Record the minimal area needed for the message.
1000 SetMinArea(MythRect(min_rect.toRect()));
1001 }
1002}
1003
1005{
1006 int lineNo = -1;
1007 int lineCount = 0;
1008 int currPos = 0;
1009 int layoutStartPos = 0;
1010 int xPos = 0;
1011
1012 for (int x = 0; x < m_layouts.count(); x++)
1013 {
1014 QTextLayout *layout = m_layouts.at(x);
1015
1016 for (int y = 0; y < layout->lineCount(); y++)
1017 {
1018 lineCount++;
1019 if (lineNo != -1)
1020 continue;
1021
1022 QTextLine line = layout->lineAt(y);
1023
1024 if (m_textCursor >= currPos && m_textCursor < currPos + line.textLength()
1025 + (line.lineNumber() == layout->lineCount() - 1 ? 1 : 0))
1026 {
1027 lineNo = lineCount - 1;
1028 xPos = line.cursorToX(m_textCursor - layoutStartPos);
1029 continue;
1030 }
1031
1032 currPos += line.textLength();
1033 }
1034
1035 currPos += 1; // skip the newline
1036 layoutStartPos = currPos;
1037 }
1038
1039 // are we are at the top and need to scroll up
1040 if (lineNo == 0 && lines < 0)
1041 return -1;
1042
1043 // are we at the bottom and need to scroll down
1044 if (lineNo == lineCount - 1 && lines > 0)
1045 return -1;
1046
1047 if (lineNo == -1)
1048 {
1049 LOG(VB_GENERAL, LOG_ERR,
1050 QString("'%1' (%2) MoveCursor offset %3 not found in ANY paragraph!")
1051 .arg(objectName(), GetXMLLocation(), QString::number(m_textCursor)));
1052 return m_textCursor;
1053 }
1054
1055 int newLine = lineNo + lines;
1056
1057 newLine = std::max(newLine, 0);
1058
1059 if (newLine >= lineCount)
1060 newLine = lineCount - 1;
1061
1062 lineNo = -1;
1063 layoutStartPos = 0;
1064
1065 for (int x = 0; x < m_layouts.count(); x++)
1066 {
1067 QTextLayout *layout = m_layouts.at(x);
1068
1069 for (int y = 0; y < layout->lineCount(); y++)
1070 {
1071 lineNo++;
1072 QTextLine line = layout->lineAt(y);
1073
1074 if (lineNo == newLine)
1075 return layoutStartPos + line.xToCursor(xPos);
1076 }
1077
1078 layoutStartPos += layout->text().length() + 1;
1079 }
1080
1081 // should never reach here
1082 return m_textCursor;
1083}
1084
1085QPoint MythUIText::CursorPosition(int text_offset)
1086{
1087 if (m_layouts.empty())
1088 return m_area.topLeft().toQPoint();
1089
1090 if (text_offset == m_textCursor)
1091 return m_cursorPos;
1092 m_textCursor = text_offset;
1093
1094 QVector<QTextLayout *>::const_iterator Ipara {};
1095 QPoint pos;
1096 int x = 0;
1097 int y = 0;
1098 int offset = text_offset;
1099
1100 for (Ipara = m_layouts.constBegin(); Ipara != m_layouts.constEnd(); ++Ipara)
1101 {
1102 QTextLine line = (*Ipara)->lineForTextPosition(offset);
1103
1104 if (line.isValid())
1105 {
1106 pos.setX(line.cursorToX(&offset));
1107 pos.setY(line.y());
1108 break;
1109 }
1110 offset -= ((*Ipara)->text().size() + 1); // Account for \n
1111 }
1112 if (Ipara == m_layouts.constEnd())
1113 {
1114 LOG(VB_GENERAL, LOG_ERR,
1115 QString("'%1' (%2) CursorPosition offset %3 not found in "
1116 "ANY paragraph!")
1117 .arg(objectName(), GetXMLLocation(), QString::number(text_offset)));
1118 return m_area.topLeft().toQPoint();
1119 }
1120
1121 int mid = m_drawRect.width() / 2;
1122 if (m_canvas.width() <= m_drawRect.width() || pos.x() <= mid)
1123 x = 0; // start
1124 else if (pos.x() >= m_canvas.width() - mid) // end
1125 {
1126 x = m_canvas.width() - m_drawRect.width();
1127 pos.setX(pos.x() - x);
1128 }
1129 else // middle
1130 {
1131 x = pos.x() - mid;
1132 pos.setX(pos.x() - x);
1133 }
1134
1135 int line_height = m_lineHeight + m_leading;
1136 mid = m_area.height() / 2;
1137 mid -= (mid % line_height);
1138 y = pos.y() - mid;
1139
1140 if (y <= 0 || m_canvas.height() <= m_area.height()) // Top of buffer
1141 y = 0;
1142 else if (y + m_area.height() > m_canvas.height()) // Bottom of buffer
1143 {
1144 int visible_lines = ((m_area.height() / line_height) * line_height);
1145 y = m_canvas.height() - visible_lines;
1146 pos.setY(visible_lines - (m_canvas.height() - pos.y()));
1147 }
1148 else // somewhere in the middle
1149 {
1150 y -= m_leading;
1151 pos.setY(mid + m_leading);
1152 }
1153
1154 m_canvas.moveTopLeft(QPoint(-x, -y));
1155
1156 // Compensate for vertical alignment
1157 pos.setY(pos.y() + m_drawRect.y() - m_area.y());
1158
1159 pos += m_area.topLeft().toQPoint();
1160 m_cursorPos = pos;
1161
1162 return pos;
1163}
1164
1166{
1167 //
1168 // Call out base class pulse which will handle any alpha cycling and
1169 // movement
1170 //
1171
1173
1174 // Calculate interval since last update in msecs - clamped to 50msecs (20Hz)
1175 // in case of UI blocking/pauses
1176 int64_t currentmsecs = QDateTime::currentMSecsSinceEpoch();
1177 int64_t interval = std::min(currentmsecs - m_lastUpdate, static_cast<int64_t>(50));
1178 m_lastUpdate = currentmsecs;
1179
1180 // Rates are pixels per second (float) normalised to 70Hz for historical reasons.
1181 // Convert interval accordingly.
1182 float rate = (interval / 1000.0F) * DEFAULT_REFRESH_RATE;
1183
1184 if (m_colorCycling)
1185 {
1186 m_curR += m_incR;
1187 m_curG += m_incG;
1188 m_curB += m_incB;
1189
1190 m_curStep++;
1191
1192 if (m_curStep >= m_numSteps)
1193 {
1194 m_curStep = 0;
1195 m_incR *= -1;
1196 m_incG *= -1;
1197 m_incB *= -1;
1198 }
1199
1200 QColor newColor = QColor(static_cast<int>(m_curR),
1201 static_cast<int>(m_curG),
1202 static_cast<int>(m_curB));
1203 if (newColor != m_font->color())
1204 {
1205 m_font->SetColor(newColor);
1206 SetRedraw();
1207 }
1208 }
1209
1210 if (m_scrolling)
1211 {
1212 if (m_scrollPause > 0.0F)
1213 m_scrollPause -= rate;
1214 else
1215 {
1216 if (m_scrollBounce)
1218 else
1220 }
1221
1222 int whole = static_cast<int>(m_scrollPos);
1223 if (m_scrollPosWhole != whole)
1224 {
1225 int shift = whole - m_scrollPosWhole;
1226 m_scrollPosWhole = whole;
1227
1228 switch (m_scrollDirection)
1229 {
1230 case ScrollLeft :
1231 if (m_canvas.width() > m_drawRect.width())
1232 {
1233 ShiftCanvas(-shift, 0);
1234 if (m_canvas.x() + m_canvas.width() < 0)
1235 {
1236 SetCanvasPosition(m_drawRect.width(), 0);
1238 }
1239 }
1240 break;
1241 case ScrollRight :
1242 if (m_canvas.width() > m_drawRect.width())
1243 {
1244 ShiftCanvas(shift, 0);
1245 if (m_canvas.x() > m_drawRect.width())
1246 {
1247 SetCanvasPosition(-m_canvas.width(), 0);
1249 }
1250 }
1251 break;
1252 case ScrollHorizontal:
1253 if (m_canvas.width() <= m_drawRect.width())
1254 break;
1255 if (m_scrollBounce) // scroll right
1256 {
1257 if (m_canvas.x() + m_scrollOffset > m_drawRect.x())
1258 {
1259 m_scrollBounce = false;
1262 }
1263 else
1264 {
1265 ShiftCanvas(shift, 0);
1266 }
1267 }
1268 else // scroll left
1269 {
1270 if (m_canvas.x() + m_canvas.width() + m_scrollOffset <
1271 m_drawRect.x() + m_drawRect.width())
1272 {
1273 m_scrollBounce = true;
1276 }
1277 else
1278 {
1279 ShiftCanvas(-shift, 0);
1280 }
1281 }
1282 break;
1283 case ScrollUp :
1284 if (m_canvas.height() > m_drawRect.height())
1285 {
1286 ShiftCanvas(0, -shift);
1287 if (m_canvas.y() + m_canvas.height() < 0)
1288 {
1289 SetCanvasPosition(0, m_drawRect.height());
1291 }
1292 }
1293 break;
1294 case ScrollDown :
1295 if (m_canvas.height() > m_drawRect.height())
1296 {
1297 ShiftCanvas(0, shift);
1298 if (m_canvas.y() > m_drawRect.height())
1299 {
1300 SetCanvasPosition(0, -m_canvas.height());
1302 }
1303 }
1304 break;
1305 case ScrollVertical:
1306 if (m_canvas.height() <= m_drawRect.height())
1307 break;
1308 if (m_scrollBounce) // scroll down
1309 {
1310 if (m_canvas.y() + m_scrollOffset > m_drawRect.y())
1311 {
1312 m_scrollBounce = false;
1315 }
1316 else
1317 {
1318 ShiftCanvas(0, shift);
1319 }
1320 }
1321 else // scroll up
1322 {
1323 if (m_canvas.y() + m_canvas.height() + m_scrollOffset <
1324 m_drawRect.y() + m_drawRect.height())
1325 {
1326 m_scrollBounce = true;
1329 }
1330 else
1331 {
1332 ShiftCanvas(0, -shift);
1333 }
1334 }
1335 break;
1336 case ScrollNone:
1337 break;
1338 }
1339 }
1340 }
1341}
1342
1343void MythUIText::CycleColor(const QColor& startColor, const QColor& endColor, int numSteps)
1344{
1345 if (!GetPainter()->SupportsAnimation())
1346 return;
1347
1348 m_startColor = startColor;
1349 m_endColor = endColor;
1350 m_numSteps = numSteps;
1351 m_curStep = 0;
1352
1353 m_curR = startColor.red();
1354 m_curG = startColor.green();
1355 m_curB = startColor.blue();
1356
1357 m_incR = (endColor.red() * 1.0F - m_curR) / m_numSteps;
1358 m_incG = (endColor.green() * 1.0F - m_curG) / m_numSteps;
1359 m_incB = (endColor.blue() * 1.0F - m_curB) / m_numSteps;
1360
1361 m_colorCycling = true;
1362}
1363
1365{
1366 if (!m_colorCycling)
1367 return;
1368
1370 m_colorCycling = false;
1371 SetRedraw();
1372}
1373
1375 const QString &filename, QDomElement &element, bool showWarnings)
1376{
1377 if (element.tagName() == "area")
1378 {
1379 SetArea(parseRect(element));
1381 }
1382 // else if (element.tagName() == "altarea") // Unused, but maybe in future?
1383 // m_altDisplayRect = parseRect(element);
1384 else if (element.tagName() == "font")
1385 {
1386 QString fontname = getFirstText(element);
1387 MythFontProperties *fp = GetFont(fontname);
1388
1389 if (!fp)
1390 fp = GetGlobalFontMap()->GetFont(fontname);
1391
1392 if (fp)
1393 {
1394 MythFontProperties font = *fp;
1396 font.Rescale(window->GetUIScreenRect().height());
1397 font.AdjustStretch(window->GetFontStretch());
1398 QString state = element.attribute("state", "");
1399
1400 if (!state.isEmpty())
1401 {
1402 m_fontStates.insert(state, font);
1403 }
1404 else
1405 {
1406 m_fontStates.insert("default", font);
1407 *m_font = m_fontStates["default"];
1408 }
1409 }
1410 }
1411 else if (element.tagName() == "extraleading")
1412 {
1413 m_extraLeading = getFirstText(element).toInt();
1414 }
1415 else if (element.tagName() == "value")
1416 {
1417 if (element.attribute("lang", "").isEmpty())
1418 {
1419 m_message = QCoreApplication::translate("ThemeUI",
1420 parseText(element).toUtf8());
1421 }
1422 else if ((element.attribute("lang", "").toLower() ==
1424 (element.attribute("lang", "").toLower() ==
1426 {
1427 m_message = parseText(element);
1428 }
1429
1432 }
1433 else if (element.tagName() == "template")
1434 {
1435 m_templateText = parseText(element);
1436 }
1437 else if (element.tagName() == "cutdown")
1438 {
1439 QString mode = getFirstText(element).toLower();
1440
1441 if (mode == "left")
1442 SetCutDown(Qt::ElideLeft);
1443 else if (mode == "middle")
1444 SetCutDown(Qt::ElideMiddle);
1445 else if (mode == "right" || parseBool(element))
1446 SetCutDown(Qt::ElideRight);
1447 else
1448 SetCutDown(Qt::ElideNone);
1449 }
1450 else if (element.tagName() == "multiline")
1451 {
1452 SetMultiLine(parseBool(element));
1453 }
1454 else if (element.tagName() == "align")
1455 {
1456 QString align = getFirstText(element).toLower();
1458 }
1459 else if (element.tagName() == "colorcycle")
1460 {
1461 if (GetPainter()->SupportsAnimation())
1462 {
1463 QString tmp = element.attribute("start");
1464
1465 if (!tmp.isEmpty())
1466 m_startColor = QColor(tmp);
1467
1468 tmp = element.attribute("end");
1469
1470 if (!tmp.isEmpty())
1471 m_endColor = QColor(tmp);
1472
1473 tmp = element.attribute("steps");
1474
1475 if (!tmp.isEmpty())
1476 m_numSteps = tmp.toInt();
1477
1478 // initialize the rest of the stuff
1480 }
1481 else
1482 {
1483 m_colorCycling = false;
1484 }
1485
1486 m_colorCycling = parseBool(element.attribute("disable"));
1487 }
1488 else if (element.tagName() == "scroll")
1489 {
1490 if (GetPainter()->SupportsAnimation())
1491 {
1492 QString tmp = element.attribute("direction");
1493
1494 if (!tmp.isEmpty())
1495 {
1496 tmp = tmp.toLower();
1497
1498 if (tmp == "left")
1500 else if (tmp == "right")
1502 else if (tmp == "up")
1504 else if (tmp == "down")
1506 else if (tmp == "horizontal")
1508 else if (tmp == "vertical")
1510 else
1511 {
1513 LOG(VB_GENERAL, LOG_ERR,
1514 QString("'%1' (%2) Invalid scroll attribute")
1515 .arg(objectName(), GetXMLLocation()));
1516 }
1517 }
1518
1519 tmp = element.attribute("startdelay");
1520 if (!tmp.isEmpty())
1521 {
1522 float seconds = tmp.toFloat();
1523 m_scrollStartDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1524 }
1525 tmp = element.attribute("returndelay");
1526 if (!tmp.isEmpty())
1527 {
1528 float seconds = tmp.toFloat();
1529 m_scrollReturnDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1530 }
1531 tmp = element.attribute("rate");
1532 if (!tmp.isEmpty())
1533 {
1534#if 0 // scroll rate as a percentage of 70Hz
1535 float percent = tmp.toFloat() / 100.0;
1536 m_scrollForwardRate = percent;
1537#else // scroll rate as pixels per second
1538 int pixels = tmp.toInt();
1539 m_scrollForwardRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1540#endif
1541 }
1542 tmp = element.attribute("returnrate");
1543 if (!tmp.isEmpty())
1544 {
1545#if 0 // scroll rate as a percentage of 70Hz
1546 float percent = tmp.toFloat() / 100.0;
1547 m_scrollReturnRate = percent;
1548#else // scroll rate as pixels per second
1549 int pixels = tmp.toInt();
1550 m_scrollReturnRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1551#endif
1552 }
1553
1554 m_scrolling = true;
1555 }
1556 else
1557 {
1558 m_scrolling = false;
1559 }
1560 }
1561 else if (element.tagName() == "case")
1562 {
1563 QString stringCase = getFirstText(element).toLower();
1564
1565 if (stringCase == "lower")
1567 else if (stringCase == "upper")
1569 else if (stringCase == "capitalisefirst")
1571 else if (stringCase == "capitaliseall")
1573 else
1575 }
1576 else
1577 {
1578 if (element.tagName() == "minsize" && element.hasAttribute("shrink"))
1579 {
1580 m_shrinkNarrow = (element.attribute("shrink")
1581 .toLower() != "short");
1582 }
1583
1584 return MythUIType::ParseElement(filename, element, showWarnings);
1585 }
1586
1587 return true;
1588}
1589
1591{
1592 auto *text = dynamic_cast<MythUIText *>(base);
1593
1594 if (!text)
1595 {
1596 LOG(VB_GENERAL, LOG_ERR,
1597 QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1598 .arg(objectName(), GetXMLLocation(),
1599 base->objectName(), base->GetXMLLocation()));
1600 return;
1601 }
1602
1603 m_justification = text->m_justification;
1604 m_origDisplayRect = text->m_origDisplayRect;
1605 m_altDisplayRect = text->m_altDisplayRect;
1606 m_canvas = text->m_canvas;
1607 m_drawRect = text->m_drawRect;
1608
1609 m_defaultMessage = text->m_defaultMessage;
1610 SetText(text->m_message);
1611 m_cutMessage = text->m_cutMessage;
1612 m_templateText = text->m_templateText;
1613
1614 m_shrinkNarrow = text->m_shrinkNarrow;
1615 m_cutdown = text->m_cutdown;
1616 m_multiLine = text->m_multiLine;
1617 m_leading = text->m_leading;
1618 m_extraLeading = text->m_extraLeading;
1619 m_lineHeight = text->m_lineHeight;
1620 m_textCursor = text->m_textCursor;
1621
1622 QMutableMapIterator<QString, MythFontProperties> it(text->m_fontStates);
1623
1624 while (it.hasNext())
1625 {
1626 it.next();
1627 m_fontStates.insert(it.key(), it.value());
1628 }
1629
1630 *m_font = m_fontStates["default"];
1631
1632 m_colorCycling = text->m_colorCycling;
1633 m_startColor = text->m_startColor;
1634 m_endColor = text->m_endColor;
1635 m_numSteps = text->m_numSteps;
1636 m_curStep = text->m_curStep;
1637 m_curR = text->m_curR;
1638 m_curG = text->m_curG;
1639 m_curB = text->m_curB;
1640 m_incR = text->m_incR;
1641 m_incG = text->m_incG;
1642 m_incB = text->m_incB;
1643
1644 m_scrollStartDelay = text->m_scrollStartDelay;
1645 m_scrollReturnDelay = text->m_scrollReturnDelay;
1646 m_scrollForwardRate = text->m_scrollForwardRate;
1647 m_scrollReturnRate = text->m_scrollReturnRate;
1648 m_scrollDirection = text->m_scrollDirection;
1649 m_scrolling = text->m_scrolling;
1650
1651 m_textCase = text->m_textCase;
1652
1655}
1656
1658{
1659 auto *text = new MythUIText(parent, objectName());
1660 text->CopyFrom(this);
1661}
1662
1663
1665{
1666 if (m_scrolling && m_cutdown != Qt::ElideNone)
1667 {
1668 LOG(VB_GENERAL, LOG_ERR,
1669 QString("'%1' (%2): <scroll> and <cutdown> are not combinable.")
1670 .arg(objectName(), GetXMLLocation()));
1671 m_cutdown = Qt::ElideNone;
1672 }
1674}
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:610
MythPoint m_minSize
Definition: mythuitype.h:279
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
void SetRedraw(void)
Definition: mythuitype.cpp:313
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:820
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:456
MythFontProperties * GetFont(const QString &text) const
int CalcAlpha(int alphamod) const
Definition: mythuitype.cpp:474
void SetPosition(int x, int y)
Convenience method, calls SetPosition(const MythPoint&) Override that instead to change functionality...
Definition: mythuitype.cpp:533
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:73
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 guint32 * tmp
Definition: goom_core.cpp:26
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:31
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
static constexpr uint8_t DEFAULT_REFRESH_RATE
Definition: mythuitext.h:14
const std::array< const std::string, 8 > formats
Definition: vbilut.cpp:189