Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MFXDynamicLabel.cpp
15 : /// @author Mirko Barthauer
16 : /// @date 31.03.2023
17 : ///
18 : // Text label with dynamic multi-line text (inserts line line breaks on the fly)
19 : /****************************************************************************/
20 : #include <config.h>
21 : #include "MFXDynamicLabel.h"
22 :
23 : // ===========================================================================
24 : // FOX callback mapping
25 : // ===========================================================================
26 :
27 : FXDEFMAP(MFXDynamicLabel) MFXLabelMap[] = {
28 : FXMAPFUNC(SEL_UPDATE, 0, MFXDynamicLabel::onUpdate),
29 : };
30 :
31 : // Object implementation
32 0 : FXIMPLEMENT(MFXDynamicLabel, FXLabel, MFXLabelMap, ARRAYNUMBER(MFXLabelMap))
33 :
34 : // ===========================================================================
35 : // method definitions
36 : // ===========================================================================
37 :
38 : // ---------------------------------------------------------------------------
39 : // MFXLabel - public methods
40 : // ---------------------------------------------------------------------------
41 :
42 0 : MFXDynamicLabel::MFXDynamicLabel(FXComposite* p, const FXString& text, FXIcon* ic, FXuint opts, FXint x, FXint y, FXint w, FXint h, FXint pl, FXint pr, FXint pt, FXint pb, std::string indent):
43 0 : FXLabel(p, text, ic, opts, x, y, w, h, pl, pr, pt, pb), myOriginalString(text.text()), myIndentString(indent), myPreviousWidth(0) {
44 0 : computeIndentation();
45 0 : }
46 :
47 :
48 : long
49 0 : MFXDynamicLabel::onUpdate(FXObject* sender, FXSelector sel, void* ptr) {
50 : const int curWidth = getWidth();
51 0 : if (myPreviousWidth - curWidth != 0 && curWidth > 1) {
52 0 : reformatLineBreaks(curWidth);
53 0 : myPreviousWidth = curWidth;
54 : }
55 0 : return FXLabel::onUpdate(sender, sel, ptr);
56 : }
57 :
58 :
59 : void
60 0 : MFXDynamicLabel::setText(const FXString& text) {
61 0 : FXLabel::setText(text);
62 0 : myOriginalString = text.text();
63 0 : computeIndentation();
64 0 : }
65 :
66 :
67 : void
68 0 : MFXDynamicLabel::position(FXint x, FXint y, FXint w, FXint h) {
69 0 : FXLabel::position(x, y, w, h);
70 0 : reformatLineBreaks(w);
71 0 : }
72 :
73 :
74 : FXint
75 0 : MFXDynamicLabel::getDefaultHeight() {
76 : int pWidth = getParent()->getWidth();
77 0 : if (pWidth > 1) { // sign that it has been initialised
78 0 : FXFrame* frame = dynamic_cast<FXFrame*>(getParent());
79 : int padding = 0;
80 0 : if (frame != nullptr) {
81 0 : padding = frame->getPadLeft() + frame->getPadRight();
82 : }
83 0 : reformatLineBreaks(pWidth - padding);
84 : }
85 0 : return FXLabel::getDefaultHeight();
86 : }
87 :
88 :
89 : void
90 0 : MFXDynamicLabel::computeIndentation() {
91 0 : if (myOriginalString.find(myIndentString) == 0 || myOriginalString.find("\n" + myIndentString) != std::string::npos) {
92 0 : myIndent = (int)myIndentString.size();
93 : } else {
94 0 : myIndent = 0;
95 : }
96 0 : }
97 :
98 :
99 : void
100 0 : MFXDynamicLabel::reformatLineBreaks(const int curWidth) {
101 0 : const int preferredWidth = curWidth - getPadLeft() - getPadRight();
102 0 : const int textWidth = getApp()->getNormalFont()->getTextWidth(label);
103 0 : if (textWidth <= preferredWidth || preferredWidth < 1) {
104 0 : return;
105 : }
106 0 : std::string newLine = "\n" + std::string(myIndent, ' ');
107 0 : const int newLineOffset = (int)newLine.size();
108 : std::string msg = myOriginalString;
109 : int pos = 0;
110 0 : int finalPos = (int)msg.size() - 1;
111 : std::string::size_type nextLineBreak;
112 0 : while (pos < finalPos) {
113 0 : nextLineBreak = msg.find('\n', pos);
114 0 : int subPos = (nextLineBreak != std::string::npos) ? (int)nextLineBreak : finalPos;
115 0 : if (getApp()->getNormalFont()->getTextWidth(msg.substr(pos, subPos - pos).c_str()) <= preferredWidth) {
116 0 : pos = subPos + 1;
117 0 : continue;
118 : }
119 0 : if (myIndent > 0 && msg.substr(pos, myIndent) == myIndentString) {
120 0 : pos += myIndent;
121 : }
122 : // select position for next line break
123 0 : const int endPos = (nextLineBreak != std::string::npos) ? (int)nextLineBreak - 1 : finalPos;
124 : std::string::size_type nextSpace = std::string::npos;
125 : int lastSpacePos = -1;
126 : int pos2 = pos;
127 0 : while (pos2 < endPos) {
128 0 : nextSpace = msg.find(' ', pos2);
129 0 : if (nextSpace != std::string::npos && (int)nextSpace <= pos + myIndent) {
130 : nextSpace = std::string::npos;
131 0 : pos2 += myIndent + 1;
132 0 : } else if (nextSpace != std::string::npos && (int)nextSpace < endPos) {
133 0 : std::string testString = msg.substr(pos, nextSpace - pos);
134 0 : if (getApp()->getNormalFont()->getTextWidth(msg.substr(pos, nextSpace - pos).c_str()) > preferredWidth) {
135 0 : if (lastSpacePos > 0) {
136 0 : msg.replace(lastSpacePos, 1, newLine);
137 0 : pos2 = lastSpacePos + newLineOffset;
138 0 : finalPos += newLineOffset;
139 : } else {
140 : msg.replace(nextSpace, 1, newLine);
141 0 : pos2 = (int)nextSpace + newLineOffset;
142 0 : finalPos += newLineOffset;
143 : }
144 : break;
145 : } else {
146 0 : pos2 = (int)nextSpace + 1;
147 0 : if (msg.find(' ', pos2) == std::string::npos && lastSpacePos > 0) { // string end condition
148 0 : msg.replace(lastSpacePos, 1, newLine);
149 0 : pos2 += newLineOffset;
150 0 : break;
151 : }
152 : }
153 : lastSpacePos = (int)nextSpace;
154 : } else {
155 0 : pos2 = endPos + 2;
156 : }
157 : }
158 : pos = pos2;
159 : }
160 0 : label = msg.c_str();
161 : }
162 :
163 :
164 0 : MFXDynamicLabel::MFXDynamicLabel() {}
|