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());
788 m_cutMessage = tmp.arg(m_message);
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 {
1125 x = 0; // start
1126 }
1127 else if (pos.x() >= m_canvas.width() - mid) // end
1128 {
1129 x = m_canvas.width() - m_drawRect.width();
1130 pos.setX(pos.x() - x);
1131 }
1132 else // middle
1133 {
1134 x = pos.x() - mid;
1135 pos.setX(pos.x() - x);
1136 }
1137
1138 int line_height = m_lineHeight + m_leading;
1139 mid = m_area.height() / 2;
1140 mid -= (mid % line_height);
1141 y = pos.y() - mid;
1142
1143 if (y <= 0 || m_canvas.height() <= m_area.height()) // Top of buffer
1144 {
1145 y = 0;
1146 }
1147 else if (y + m_area.height() > m_canvas.height()) // Bottom of buffer
1148 {
1149 int visible_lines = ((m_area.height() / line_height) * line_height);
1150 y = m_canvas.height() - visible_lines;
1151 pos.setY(visible_lines - (m_canvas.height() - pos.y()));
1152 }
1153 else // somewhere in the middle
1154 {
1155 y -= m_leading;
1156 pos.setY(mid + m_leading);
1157 }
1158
1159 m_canvas.moveTopLeft(QPoint(-x, -y));
1160
1161 // Compensate for vertical alignment
1162 pos.setY(pos.y() + m_drawRect.y() - m_area.y());
1163
1164 pos += m_area.topLeft().toQPoint();
1165 m_cursorPos = pos;
1166
1167 return pos;
1168}
1169
1171{
1172 //
1173 // Call out base class pulse which will handle any alpha cycling and
1174 // movement
1175 //
1176
1178
1179 // Calculate interval since last update in msecs - clamped to 50msecs (20Hz)
1180 // in case of UI blocking/pauses
1181 int64_t currentmsecs = QDateTime::currentMSecsSinceEpoch();
1182 int64_t interval = std::min(currentmsecs - m_lastUpdate, static_cast<int64_t>(50));
1183 m_lastUpdate = currentmsecs;
1184
1185 // Rates are pixels per second (float) normalised to 70Hz for historical reasons.
1186 // Convert interval accordingly.
1187 float rate = (interval / 1000.0F) * DEFAULT_REFRESH_RATE;
1188
1189 if (m_colorCycling)
1190 {
1191 m_curR += m_incR;
1192 m_curG += m_incG;
1193 m_curB += m_incB;
1194
1195 m_curStep++;
1196
1197 if (m_curStep >= m_numSteps)
1198 {
1199 m_curStep = 0;
1200 m_incR *= -1;
1201 m_incG *= -1;
1202 m_incB *= -1;
1203 }
1204
1205 QColor newColor = QColor(static_cast<int>(m_curR),
1206 static_cast<int>(m_curG),
1207 static_cast<int>(m_curB));
1208 if (newColor != m_font->color())
1209 {
1210 m_font->SetColor(newColor);
1211 SetRedraw();
1212 }
1213 }
1214
1215 if (m_scrolling)
1216 {
1217 if (m_scrollPause > 0.0F)
1218 {
1219 m_scrollPause -= rate;
1220 }
1221 else
1222 {
1223 if (m_scrollBounce)
1225 else
1227 }
1228
1229 int whole = static_cast<int>(m_scrollPos);
1230 if (m_scrollPosWhole != whole)
1231 {
1232 int shift = whole - m_scrollPosWhole;
1233 m_scrollPosWhole = whole;
1234
1235 switch (m_scrollDirection)
1236 {
1237 case ScrollLeft :
1238 if (m_canvas.width() > m_drawRect.width())
1239 {
1240 ShiftCanvas(-shift, 0);
1241 if (m_canvas.x() + m_canvas.width() < 0)
1242 {
1243 SetCanvasPosition(m_drawRect.width(), 0);
1245 }
1246 }
1247 break;
1248 case ScrollRight :
1249 if (m_canvas.width() > m_drawRect.width())
1250 {
1251 ShiftCanvas(shift, 0);
1252 if (m_canvas.x() > m_drawRect.width())
1253 {
1254 SetCanvasPosition(-m_canvas.width(), 0);
1256 }
1257 }
1258 break;
1259 case ScrollHorizontal:
1260 if (m_canvas.width() <= m_drawRect.width())
1261 break;
1262 if (m_scrollBounce) // scroll right
1263 {
1264 if (m_canvas.x() + m_scrollOffset > m_drawRect.x())
1265 {
1266 m_scrollBounce = false;
1269 }
1270 else
1271 {
1272 ShiftCanvas(shift, 0);
1273 }
1274 }
1275 else // scroll left
1276 {
1277 if (m_canvas.x() + m_canvas.width() + m_scrollOffset <
1278 m_drawRect.x() + m_drawRect.width())
1279 {
1280 m_scrollBounce = true;
1283 }
1284 else
1285 {
1286 ShiftCanvas(-shift, 0);
1287 }
1288 }
1289 break;
1290 case ScrollUp :
1291 if (m_canvas.height() > m_drawRect.height())
1292 {
1293 ShiftCanvas(0, -shift);
1294 if (m_canvas.y() + m_canvas.height() < 0)
1295 {
1296 SetCanvasPosition(0, m_drawRect.height());
1298 }
1299 }
1300 break;
1301 case ScrollDown :
1302 if (m_canvas.height() > m_drawRect.height())
1303 {
1304 ShiftCanvas(0, shift);
1305 if (m_canvas.y() > m_drawRect.height())
1306 {
1307 SetCanvasPosition(0, -m_canvas.height());
1309 }
1310 }
1311 break;
1312 case ScrollVertical:
1313 if (m_canvas.height() <= m_drawRect.height())
1314 break;
1315 if (m_scrollBounce) // scroll down
1316 {
1317 if (m_canvas.y() + m_scrollOffset > m_drawRect.y())
1318 {
1319 m_scrollBounce = false;
1322 }
1323 else
1324 {
1325 ShiftCanvas(0, shift);
1326 }
1327 }
1328 else // scroll up
1329 {
1330 if (m_canvas.y() + m_canvas.height() + m_scrollOffset <
1331 m_drawRect.y() + m_drawRect.height())
1332 {
1333 m_scrollBounce = true;
1336 }
1337 else
1338 {
1339 ShiftCanvas(0, -shift);
1340 }
1341 }
1342 break;
1343 case ScrollNone:
1344 break;
1345 }
1346 }
1347 }
1348}
1349
1350void MythUIText::CycleColor(const QColor& startColor, const QColor& endColor, int numSteps)
1351{
1352 if (!GetPainter()->SupportsAnimation())
1353 return;
1354
1355 m_startColor = startColor;
1356 m_endColor = endColor;
1357 m_numSteps = numSteps;
1358 m_curStep = 0;
1359
1360#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1361 m_curR = startColor.red();
1362 m_curG = startColor.green();
1363 m_curB = startColor.blue();
1364
1365 m_incR = ((endColor.red() * 1.0F) - m_curR) / m_numSteps;
1366 m_incG = ((endColor.green() * 1.0F) - m_curG) / m_numSteps;
1367 m_incB = ((endColor.blue() * 1.0F) - m_curB) / m_numSteps;
1368#else
1369 m_curR = startColor.redF();
1370 m_curG = startColor.greenF();
1371 m_curB = startColor.blueF();
1372
1373 m_incR = (endColor.redF() - m_curR) / m_numSteps;
1374 m_incG = (endColor.greenF() - m_curG) / m_numSteps;
1375 m_incB = (endColor.blueF() - m_curB) / m_numSteps;
1376#endif
1377 m_colorCycling = true;
1378}
1379
1381{
1382 if (!m_colorCycling)
1383 return;
1384
1386 m_colorCycling = false;
1387 SetRedraw();
1388}
1389
1391 const QString &filename, QDomElement &element, bool showWarnings)
1392{
1393 if (element.tagName() == "area")
1394 {
1395 SetArea(parseRect(element));
1397 }
1398 // else if (element.tagName() == "altarea") // Unused, but maybe in future?
1399 // m_altDisplayRect = parseRect(element);
1400 else if (element.tagName() == "font")
1401 {
1402 QString fontname = getFirstText(element);
1403 MythFontProperties *fp = GetFont(fontname);
1404
1405 if (!fp)
1406 fp = GetGlobalFontMap()->GetFont(fontname);
1407
1408 if (fp)
1409 {
1410 MythFontProperties font = *fp;
1412 font.Rescale(window->GetUIScreenRect().height());
1413 font.AdjustStretch(window->GetFontStretch());
1414 QString state = element.attribute("state", "");
1415
1416 if (!state.isEmpty())
1417 {
1418 m_fontStates.insert(state, font);
1419 }
1420 else
1421 {
1422 m_fontStates.insert("default", font);
1423 *m_font = m_fontStates["default"];
1424 }
1425 }
1426 }
1427 else if (element.tagName() == "extraleading")
1428 {
1429 m_extraLeading = getFirstText(element).toInt();
1430 }
1431 else if (element.tagName() == "value")
1432 {
1433 if (element.attribute("lang", "").isEmpty())
1434 {
1435 m_message = QCoreApplication::translate("ThemeUI",
1436 parseText(element).toUtf8().constData());
1437 }
1438 else if ((element.attribute("lang", "").toLower() ==
1440 (element.attribute("lang", "").toLower() ==
1442 {
1443 m_message = parseText(element);
1444 }
1445
1448 }
1449 else if (element.tagName() == "template")
1450 {
1451 m_templateText = parseText(element);
1452 }
1453 else if (element.tagName() == "cutdown")
1454 {
1455 QString mode = getFirstText(element).toLower();
1456
1457 if (mode == "left")
1458 SetCutDown(Qt::ElideLeft);
1459 else if (mode == "middle")
1460 SetCutDown(Qt::ElideMiddle);
1461 else if (mode == "right" || parseBool(element))
1462 SetCutDown(Qt::ElideRight);
1463 else
1464 SetCutDown(Qt::ElideNone);
1465 }
1466 else if (element.tagName() == "multiline")
1467 {
1468 SetMultiLine(parseBool(element));
1469 }
1470 else if (element.tagName() == "align")
1471 {
1472 QString align = getFirstText(element).toLower();
1474 }
1475 else if (element.tagName() == "colorcycle")
1476 {
1477 if (GetPainter()->SupportsAnimation())
1478 {
1479 QString tmp = element.attribute("start");
1480
1481 if (!tmp.isEmpty())
1482 m_startColor = QColor(tmp);
1483
1484 tmp = element.attribute("end");
1485
1486 if (!tmp.isEmpty())
1487 m_endColor = QColor(tmp);
1488
1489 tmp = element.attribute("steps");
1490
1491 if (!tmp.isEmpty())
1492 m_numSteps = tmp.toInt();
1493
1494 // initialize the rest of the stuff
1496 }
1497 else
1498 {
1499 m_colorCycling = false;
1500 }
1501
1502 m_colorCycling = parseBool(element.attribute("disable"));
1503 }
1504 else if (element.tagName() == "scroll")
1505 {
1506 if (GetPainter()->SupportsAnimation())
1507 {
1508 QString tmp = element.attribute("direction");
1509
1510 if (!tmp.isEmpty())
1511 {
1512 tmp = tmp.toLower();
1513
1514 if (tmp == "left") {
1516 } else if (tmp == "right") {
1518 } else if (tmp == "up") {
1520 } else if (tmp == "down") {
1522 } else if (tmp == "horizontal") {
1524 } else if (tmp == "vertical") {
1526 } else {
1528 LOG(VB_GENERAL, LOG_ERR,
1529 QString("'%1' (%2) Invalid scroll attribute")
1530 .arg(objectName(), GetXMLLocation()));
1531 }
1532 }
1533
1534 tmp = element.attribute("startdelay");
1535 if (!tmp.isEmpty())
1536 {
1537 float seconds = tmp.toFloat();
1538 m_scrollStartDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1539 }
1540 tmp = element.attribute("returndelay");
1541 if (!tmp.isEmpty())
1542 {
1543 float seconds = tmp.toFloat();
1544 m_scrollReturnDelay = static_cast<int>(lroundf(seconds * DEFAULT_REFRESH_RATE));
1545 }
1546 tmp = element.attribute("rate");
1547 if (!tmp.isEmpty())
1548 {
1549#if 0 // scroll rate as a percentage of 70Hz
1550 float percent = tmp.toFloat() / 100.0;
1551 m_scrollForwardRate = percent;
1552#else // scroll rate as pixels per second
1553 int pixels = tmp.toInt();
1554 m_scrollForwardRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1555#endif
1556 }
1557 tmp = element.attribute("returnrate");
1558 if (!tmp.isEmpty())
1559 {
1560#if 0 // scroll rate as a percentage of 70Hz
1561 float percent = tmp.toFloat() / 100.0;
1562 m_scrollReturnRate = percent;
1563#else // scroll rate as pixels per second
1564 int pixels = tmp.toInt();
1565 m_scrollReturnRate = pixels / static_cast<float>(DEFAULT_REFRESH_RATE);
1566#endif
1567 }
1568
1569 m_scrolling = true;
1570 }
1571 else
1572 {
1573 m_scrolling = false;
1574 }
1575 }
1576 else if (element.tagName() == "case")
1577 {
1578 QString stringCase = getFirstText(element).toLower();
1579
1580 if (stringCase == "lower")
1582 else if (stringCase == "upper")
1584 else if (stringCase == "capitalisefirst")
1586 else if (stringCase == "capitaliseall")
1588 else
1590 }
1591 else
1592 {
1593 if (element.tagName() == "minsize" && element.hasAttribute("shrink"))
1594 {
1595 m_shrinkNarrow = (element.attribute("shrink")
1596 .toLower() != "short");
1597 }
1598
1599 return MythUIType::ParseElement(filename, element, showWarnings);
1600 }
1601
1602 return true;
1603}
1604
1606{
1607 auto *text = dynamic_cast<MythUIText *>(base);
1608
1609 if (!text)
1610 {
1611 LOG(VB_GENERAL, LOG_ERR,
1612 QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1613 .arg(objectName(), GetXMLLocation(),
1614 base->objectName(), base->GetXMLLocation()));
1615 return;
1616 }
1617
1618 m_justification = text->m_justification;
1619 m_origDisplayRect = text->m_origDisplayRect;
1620 m_altDisplayRect = text->m_altDisplayRect;
1621 m_canvas = text->m_canvas;
1622 m_drawRect = text->m_drawRect;
1623
1624 m_defaultMessage = text->m_defaultMessage;
1625 SetText(text->m_message);
1626 m_cutMessage = text->m_cutMessage;
1627 m_templateText = text->m_templateText;
1628
1629 m_shrinkNarrow = text->m_shrinkNarrow;
1630 m_cutdown = text->m_cutdown;
1631 m_multiLine = text->m_multiLine;
1632 m_leading = text->m_leading;
1633 m_extraLeading = text->m_extraLeading;
1634 m_lineHeight = text->m_lineHeight;
1635 m_textCursor = text->m_textCursor;
1636
1637 for (auto it = text->m_fontStates.cbegin(); it != text->m_fontStates.cend(); ++it)
1638 m_fontStates.insert(it.key(), it.value());
1639
1640 *m_font = m_fontStates["default"];
1641
1642 m_colorCycling = text->m_colorCycling;
1643 m_startColor = text->m_startColor;
1644 m_endColor = text->m_endColor;
1645 m_numSteps = text->m_numSteps;
1646 m_curStep = text->m_curStep;
1647 m_curR = text->m_curR;
1648 m_curG = text->m_curG;
1649 m_curB = text->m_curB;
1650 m_incR = text->m_incR;
1651 m_incG = text->m_incG;
1652 m_incB = text->m_incB;
1653
1654 m_scrollStartDelay = text->m_scrollStartDelay;
1655 m_scrollReturnDelay = text->m_scrollReturnDelay;
1656 m_scrollForwardRate = text->m_scrollForwardRate;
1657 m_scrollReturnRate = text->m_scrollReturnRate;
1658 m_scrollDirection = text->m_scrollDirection;
1659 m_scrolling = text->m_scrolling;
1660
1661 m_textCase = text->m_textCase;
1662
1665}
1666
1668{
1669 auto *text = new MythUIText(parent, objectName());
1670 text->CopyFrom(this);
1671}
1672
1673
1675{
1676 if (m_scrolling && m_cutdown != Qt::ElideNone)
1677 {
1678 LOG(VB_GENERAL, LOG_ERR,
1679 QString("'%1' (%2): <scroll> and <cutdown> are not combinable.")
1680 .arg(objectName(), GetXMLLocation()));
1681 m_cutdown = Qt::ElideNone;
1682 }
1684}
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:51
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:81
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:46
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:97
bool m_enableInitiator
Definition: mythuitype.h:278
bool m_initiator
Definition: mythuitype.h:279
virtual MythPainter * GetPainter(void)
QString GetXMLLocation(void) const
Definition: mythuitype.h:193
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:596
MythPoint m_minSize
Definition: mythuitype.h:290
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
void SetRedraw(void)
Definition: mythuitype.cpp:299
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:806
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:442
MythFontProperties * GetFont(const QString &text) const
int CalcAlpha(int alphamod) const
Definition: mythuitype.cpp:460
void SetPosition(int x, int y)
Convenience method, calls SetPosition(const MythPoint&) Override that instead to change functionality...
Definition: mythuitype.cpp:519
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:71
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:288
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)
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