Skip to content

Commit 5935fa5

Browse files
authored
Allow SpannableString for event titles and locations (alamkanak#152)
* Introduce ResolvedWeekViewEvent to only resolve resources once * Move text conversion methods into extension methods * Allow SpannableStrings in event titles and locations * Fix incorrect drawing of event chip borders, titles, and locations
1 parent cc02252 commit 5935fa5

28 files changed

+418
-387
lines changed

core/src/main/java/com/alamkanak/weekview/AllDayEventsUpdater.kt

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
11
package com.alamkanak.weekview
22

33
import android.graphics.RectF
4-
import android.graphics.Typeface
54
import android.text.SpannableStringBuilder
65
import android.text.StaticLayout
7-
import android.text.TextPaint
8-
import android.text.TextUtils
9-
import android.text.TextUtils.TruncateAt.END
10-
import android.text.style.StyleSpan
11-
import com.alamkanak.weekview.WeekViewEvent.TextResource
126
import kotlin.math.roundToInt
137

148
internal class AllDayEventsUpdater<T : Any>(
159
private val view: WeekView<T>,
1610
private val config: WeekViewConfigWrapper,
1711
private val cache: WeekViewCache<T>,
18-
private val chipsCache: EventChipsCache<T>,
19-
private val emojiTextProcessor: EmojiTextProcessor = EmojiTextProcessor()
12+
private val chipsCache: EventChipsCache<T>
2013
) : Updater {
2114

22-
private val context = view.context
23-
private val rectCalculator = EventChipRectCalculator<T>(config)
15+
private val boundsCalculator = EventChipBoundsCalculator<T>(config)
16+
private val spannableStringBuilder = SpannableStringBuilder()
2417

2518
private var previousHorizontalOrigin: Float? = null
2619
private var dummyTextLayout: StaticLayout? = null
@@ -61,13 +54,13 @@ internal class AllDayEventsUpdater<T : Any>(
6154
eventChip: EventChip<T>,
6255
startPixel: Float
6356
) {
64-
val chipRect = rectCalculator.calculateAllDayEvent(eventChip, startPixel)
57+
val chipRect = boundsCalculator.calculateAllDayEvent(eventChip, startPixel)
6558
eventChip.bounds = if (chipRect.isValidEventBounds) chipRect else null
6659

6760
if (chipRect.isValidEventBounds) {
6861
val textLayout = calculateChipTextLayout(eventChip)
69-
textLayout?.let { layout ->
70-
cache.allDayEventLayouts[eventChip] = layout
62+
if (textLayout != null) {
63+
cache.allDayEventLayouts[eventChip] = textLayout
7164
}
7265
}
7366
}
@@ -96,31 +89,25 @@ internal class AllDayEventsUpdater<T : Any>(
9689
return dummyTextLayout
9790
}
9891

99-
val title = when (val resource = event.titleResource) {
100-
is TextResource.Id -> context.getString(resource.resId)
101-
is TextResource.Value -> resource.text
102-
null -> ""
103-
}
104-
105-
val modifiedTitle = emojiTextProcessor.process(title)
106-
val text = SpannableStringBuilder(modifiedTitle)
107-
text.setSpan(StyleSpan(Typeface.BOLD))
92+
spannableStringBuilder.clear()
93+
val title = event.title.emojify()
94+
spannableStringBuilder.append(title)
10895

109-
val location = when (val resource = event.locationResource) {
110-
is TextResource.Id -> context.getString(resource.resId)
111-
is TextResource.Value -> resource.text
112-
null -> null
113-
}
96+
// val title = event.title.emojify()
97+
// val text = SpannableStringBuilder(title)
98+
// text.setSpan(StyleSpan(Typeface.BOLD))
11499

100+
val location = event.location?.emojify()
115101
if (location != null) {
116-
val modifiedLocation = emojiTextProcessor.process(location)
117-
text.append(' ').append(modifiedLocation)
102+
spannableStringBuilder.append(' ')
103+
spannableStringBuilder.append(location)
118104
}
119105

106+
val text = spannableStringBuilder.build()
120107
val availableWidth = width.toInt()
121108

122-
val textPaint = event.getTextPaint(context, config)
123-
val textLayout = TextLayoutBuilder.build(text, textPaint, availableWidth)
109+
val textPaint = config.getTextPaint(event)
110+
val textLayout = text.toTextLayout(textPaint, availableWidth)
124111
val lineHeight = textLayout.height / textLayout.lineCount
125112

126113
// For an all day event, we display just one line
@@ -134,11 +121,11 @@ internal class AllDayEventsUpdater<T : Any>(
134121
* Creates a dummy text layout that is only used to determine the height of all-day events.
135122
*/
136123
private fun createDummyTextLayout(
137-
event: WeekViewEvent<T>
124+
event: ResolvedWeekViewEvent<T>
138125
): StaticLayout {
139126
if (dummyTextLayout == null) {
140-
val textPaint = event.getTextPaint(context, config)
141-
dummyTextLayout = TextLayoutBuilder.build("", textPaint, width = 0)
127+
val textPaint = config.getTextPaint(event)
128+
dummyTextLayout = "".toTextLayout(textPaint, width = 0)
142129
}
143130
return checkNotNull(dummyTextLayout)
144131
}
@@ -148,7 +135,7 @@ internal class AllDayEventsUpdater<T : Any>(
148135
availableWidth: Int,
149136
existingTextLayout: StaticLayout
150137
): StaticLayout {
151-
val textPaint = event.getTextPaint(context, config)
138+
val textPaint = config.getTextPaint(event)
152139
val bounds = checkNotNull(bounds)
153140
val width = bounds.width().roundToInt() - (config.eventPaddingHorizontal * 2)
154141

@@ -160,7 +147,7 @@ internal class AllDayEventsUpdater<T : Any>(
160147
return existingTextLayout
161148
}
162149

163-
return TextLayoutBuilder.build(ellipsized, textPaint, width)
150+
return ellipsized.toTextLayout(textPaint, width)
164151
}
165152

166153
private val RectF.isValidEventBounds: Boolean
@@ -169,18 +156,4 @@ internal class AllDayEventsUpdater<T : Any>(
169156
top < view.height &&
170157
right > config.timeColumnWidth &&
171158
bottom > 0)
172-
173-
private operator fun RectF.component1() = left
174-
175-
private operator fun RectF.component2() = top
176-
177-
private operator fun RectF.component3() = right
178-
179-
private operator fun RectF.component4() = bottom
180-
181-
private fun CharSequence.ellipsized(
182-
textPaint: TextPaint,
183-
availableArea: Int,
184-
truncateAt: TextUtils.TruncateAt = END
185-
): CharSequence = TextUtils.ellipsize(this, textPaint, availableArea.toFloat(), truncateAt)
186159
}

core/src/main/java/com/alamkanak/weekview/CanvasExtensions.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.alamkanak.weekview
22

33
import android.graphics.Canvas
4+
import android.graphics.RectF
45

56
fun Canvas.withTranslation(x: Float, y: Float, block: Canvas.() -> Unit) {
67
save()
@@ -21,3 +22,9 @@ fun Canvas.drawInRect(
2122
block()
2223
restore()
2324
}
25+
26+
fun RectF.insetBy(inset: Float): RectF {
27+
return RectF(this).apply {
28+
inset(inset, inset)
29+
}
30+
}

core/src/main/java/com/alamkanak/weekview/EmojiTextProcessor.kt

Lines changed: 0 additions & 16 deletions
This file was deleted.

core/src/main/java/com/alamkanak/weekview/EventChip.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,29 @@ package com.alamkanak.weekview
33
import android.graphics.RectF
44

55
/**
6-
* This class encapsulates a [WeekViewEvent] and its visual representation, a [RectF]
6+
* This class encapsulates a [ResolvedWeekViewEvent] and its visual representation, a [RectF]
77
* which is eventually drawn to the screen.
88
*
99
* There may be more than one [EventChip] for any even (think multi-day events). In that case,
10-
* multiple [EventChip]s will be used for a single [WeekViewEvent].
10+
* multiple [EventChip]s will be used for a single [ResolvedWeekViewEvent].
1111
*
12-
* The original [WeekViewEvent] is accessible via [originalEvent]. The [WeekViewEvent] that
13-
* corresponds to the drawn rectangle is accessible via [event].
12+
* The original [ResolvedWeekViewEvent] is accessible via [originalEvent]. The
13+
* [ResolvedWeekViewEvent] that corresponds to the drawn rectangle is accessible via [event].
1414
*/
1515
internal data class EventChip<T>(
1616
/**
17-
* The [WeekViewEvent] corresponding to the drawn rectangle. It might differ from
17+
* The [ResolvedWeekViewEvent] corresponding to the drawn rectangle. It might differ from
1818
* [originalEvent], which may be a multi-day event.
1919
*/
20-
val event: WeekViewEvent<T>,
20+
val event: ResolvedWeekViewEvent<T>,
2121
/**
22-
* The original [WeekViewEvent], which may be a multi-day event.
22+
* The original [ResolvedWeekViewEvent], which may be a multi-day event.
2323
*/
24-
val originalEvent: WeekViewEvent<T>
24+
val originalEvent: ResolvedWeekViewEvent<T>
2525
) {
2626

2727
/**
28-
* The rectangle in which the [WeekViewEvent] will be drawn.
28+
* The rectangle in which the [ResolvedWeekViewEvent] will be drawn.
2929
*/
3030
var bounds: RectF? = null
3131

core/src/main/java/com/alamkanak/weekview/EventChipRectCalculator.kt renamed to core/src/main/java/com/alamkanak/weekview/EventChipBoundsCalculator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.alamkanak.weekview
22

33
import android.graphics.RectF
44

5-
internal class EventChipRectCalculator<T>(
5+
internal class EventChipBoundsCalculator<T>(
66
private val config: WeekViewConfigWrapper
77
) {
88

0 commit comments

Comments
 (0)