Skip to content

Commit

Permalink
Add more information and configurability to ScheduleView
Browse files Browse the repository at this point in the history
  • Loading branch information
svenreimers committed Sep 15, 2023
1 parent c0098cd commit 0cc7172
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 45 deletions.
1 change: 1 addition & 0 deletions conference-stepengine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation 'de.jensd:fontawesomefx-fontawesome:4.7.0-11'
implementation 'org.slf4j:slf4j-api'
implementation project(':tweetwallfx-controls')
implementation project(':tweetwallfx-emoji')
implementation project(':tweetwallfx-stepengine-dataproviders')
implementation project(':tweetwallfx-transitions')
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class SessionData {
public final List<Speaker> speakerObjects;
public final int favouritesCount;
public final String trackImageUrl;
public final List<String> tags;

private SessionData(final ScheduleSlot slot) {
this.room = slot.getRoom();
Expand All @@ -76,6 +77,7 @@ private SessionData(final ScheduleSlot slot) {
.findFirst()
.orElse(0);
this.trackImageUrl = talk.getTrack().getAvatarURL();
this.tags = talk.getTags();
}

public static List<SessionData> from(final List<ScheduleSlot> slots, final OffsetTime now, final ZoneId zoneId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;
import java.util.stream.Collectors;
import javafx.animation.FadeTransition;
import javafx.application.Platform;
import javafx.geometry.Pos;
Expand All @@ -39,6 +38,7 @@
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
Expand All @@ -50,11 +50,13 @@
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tweetwallfx.conference.api.Speaker;
import org.tweetwallfx.conference.stepengine.dataprovider.ScheduleDataProvider;
import org.tweetwallfx.conference.stepengine.dataprovider.SessionData;
import org.tweetwallfx.conference.stepengine.dataprovider.SpeakerImageProvider;
import org.tweetwallfx.conference.stepengine.dataprovider.TrackImageDataProvider;
import org.tweetwallfx.controls.WordleSkin;
import org.tweetwallfx.emoji.control.EmojiFlow;
import org.tweetwallfx.stepengine.api.DataProvider;
import org.tweetwallfx.stepengine.api.Step;
import org.tweetwallfx.stepengine.api.StepEngine.MachineContext;
Expand All @@ -68,8 +70,8 @@ public class ShowSchedule implements Step {

private static final Logger LOGGER = LoggerFactory.getLogger(ShowSchedule.class);
private static final ZoneId ZONE_ID = Optional.ofNullable(System.getProperty("org.tweetwallfx.scheduledata.zone"))
.map(ZoneId::of)
.orElseGet(ZoneId::systemDefault);
.map(ZoneId::of)
.orElseGet(ZoneId::systemDefault);
private static final DateTimeFormatter HOUR_MINUTES = DateTimeFormatter.ofPattern("HH:mm");
private final Config config;

Expand Down Expand Up @@ -115,9 +117,18 @@ public void doStep(final MachineContext context) {
int row = 0;

Iterator<SessionData> iterator = dataProvider.getFilteredSessionData().iterator();
String oldRoom = null;
while (iterator.hasNext()) {
Pane sessionPane = createSessionNode(context, iterator.next());
double sessionWidth = (config.width - config.sessionHGap) / 2.0;
var sessionData = iterator.next();
if (null == oldRoom) {
oldRoom = sessionData.room.getName();
}
if (config.autoSeparateRoomTypes && col != 0 && !oldRoom.startsWith(sessionData.room.getName().substring(0, 2))) {
row++;
col = 0;
}
Pane sessionPane = createSessionNode(context, sessionData);
double sessionWidth = (config.width - (config.columns - 1) * config.sessionHGap) / config.columns;
sessionPane.setMinWidth(sessionWidth);
sessionPane.setMaxWidth(sessionWidth);
sessionPane.setPrefWidth(sessionWidth);
Expand All @@ -127,10 +138,12 @@ public void doStep(final MachineContext context) {
sessionPane.setLayoutX(col * (sessionWidth + config.sessionHGap));
sessionPane.setLayoutY(config.titleHeight + config.sessionVGap + (config.sessionHeight + config.sessionVGap) * row);
scheduleNode.getChildren().add(sessionPane);
col = (col == 0) ? 1 : 0;
if (col == 0) {
col++;
if (col == config.columns) {
row++;
col = 0;
}
oldRoom = sessionData.room.getName();
}

Platform.runLater(() -> {
Expand All @@ -151,10 +164,23 @@ public java.time.Duration preferredStepDuration(final MachineContext context) {
}

private Pane createSessionNode(final MachineContext context, final SessionData sessionData) {
var speakerNames = new Label(sessionData.speakers.stream().collect(Collectors.joining(", ")));
speakerNames.setWrapText(true);
speakerNames.setTextAlignment(TextAlignment.RIGHT);
speakerNames.getStyleClass().add("speakerName");
var speakerNames = new VBox();
speakerNames.getStyleClass().add("speakerNames");

sessionData.speakerObjects.stream().forEach(speaker -> {
var speakerName = new Label(speaker.getFullName());
speakerName.setTextAlignment(TextAlignment.RIGHT);
speakerName.getStyleClass().add("speakerName");
speakerNames.getChildren().add(speakerName);
if (config.showCompanyName) {
speaker.getCompany().ifPresent(company -> {
var companyName = new Label("(" + company + ")");
companyName.setTextAlignment(TextAlignment.RIGHT);
companyName.getStyleClass().add("companyName");
speakerNames.getChildren().add(companyName);
});
}
});

var room = new Label(sessionData.room.getName());
room.getStyleClass().add("room");
Expand All @@ -169,31 +195,25 @@ private Pane createSessionNode(final MachineContext context, final SessionData s

if (config.showAvatar) {
var speakerImageProvider = context.getDataProvider(SpeakerImageProvider.class);
var speakerImages = new HBox(config.avatarSpacing, sessionData.speakerObjects.stream()
.map(speakerImageProvider::getSpeakerImage)
.map(ImageView::new)
.peek(img -> {
// general image sizing
img.getStyleClass().add("speakerImage");
img.setFitHeight(config.avatarSize);
img.setFitWidth(config.avatarSize);
})
.peek(img -> {
// avatar image clipping
if (config.circularAvatar) {
Circle circle = new Circle(config.avatarSize/2f, config.avatarSize/2f, config.avatarSize/2f);
img.setClip(circle);
} else {
Rectangle clip = new Rectangle(config.avatarSize, config.avatarSize);
clip.setArcWidth(config.avatarArcSize);
clip.setArcHeight(config.avatarArcSize);
img.setClip(clip);
}
})
.toArray(Node[]::new)
);

topLeft = new HBox(4, topLeftVBox, speakerImages);
if (config.compressedAvatars && sessionData.speakerObjects.size() >= config.compressedAvatarsLimit) {
var speakerImages = new Pane();
var images = sessionData.speakerObjects.stream()
.map(speaker -> createSpeakerImage(speakerImageProvider, speaker))
.toList();
for (int i = 0; i < images.size(); i++) {
var image = images.get(i);
image.setLayoutX(i * (config.avatarSize * 3 / 4d + 2));
image.setLayoutY(i % 2 * config.avatarSize / 2d + 2);
speakerImages.getChildren().add(image);
}
topLeft = new HBox(4, topLeftVBox, speakerImages);
} else {
var speakerImages = new HBox(config.avatarSpacing, sessionData.speakerObjects.stream()
.map(speaker -> createSpeakerImage(speakerImageProvider, speaker))
.toArray(Node[]::new)
);
topLeft = new HBox(4, topLeftVBox, speakerImages);
}
}

if (config.showFavourite && sessionData.favouritesCount >= 0) {
Expand All @@ -209,9 +229,10 @@ private Pane createSessionNode(final MachineContext context, final SessionData s
topLeftVBox.getChildren().add(favourites);
}

var title = new Label(sessionData.title);
title.setWrapText(true);
title.setAlignment(Pos.BOTTOM_LEFT);
var title = new EmojiFlow();
title.setText(sessionData.title);
title.setEmojiFitWidth(15);
title.setEmojiFitHeight(15);
title.getStyleClass().add("title");
title.setMaxHeight(Double.MAX_VALUE);

Expand All @@ -236,7 +257,28 @@ private Pane createSessionNode(final MachineContext context, final SessionData s
var bpSessionBottomPane = new BorderPane();
bpSessionBottomPane.getStyleClass().add("sessionBottomPane");
bpSessionBottomPane.setRight(trackImageView);
bpSessionBottomPane.setCenter(bpTitle);
if (config.showTags) {
var tags = new FlowPane();
tags.getStyleClass().add("tags");
sessionData.tags.stream().forEach(tag -> {
var tagLabel = new Label(tag);
tagLabel.getStyleClass().add("tagLabel");
tags.getChildren().add(tagLabel);
});

var bpTags = new BorderPane();
bpTags.getStyleClass().add("tagPane");
BorderPane.setAlignment(tags, Pos.BOTTOM_LEFT);
bpTags.setLeft(tags);

VBox bpCenter = new VBox(bpTitle, bpTags);
bpCenter.getStyleClass().add("centerFlow");

bpSessionBottomPane.setCenter(bpCenter);
} else {
bpSessionBottomPane.setRight(trackImageView);
bpSessionBottomPane.setCenter(bpTitle);
}

var bpSessionPane = new BorderPane();
bpSessionPane.getStyleClass().add("scheduleSession");
Expand All @@ -250,6 +292,30 @@ private Pane createSessionNode(final MachineContext context, final SessionData s
return bpSessionPane;
}

private Node createSpeakerImage(SpeakerImageProvider speakerImageProvider, Speaker speaker) {
var image = speakerImageProvider.getSpeakerImage(speaker);
var speakerImage = new ImageView(image);
speakerImage.getStyleClass().add("speakerImage");
speakerImage.setPreserveRatio(true);
if (image.getWidth() > image.getHeight()) {
speakerImage.setFitHeight(config.avatarSize);
} else {
speakerImage.setFitWidth(config.avatarSize);
}

// avatar image clipping
if (config.circularAvatar) {
Circle circle = new Circle(config.avatarSize / 2f, config.avatarSize / 2f, config.avatarSize / 2f);
speakerImage.setClip(circle);
} else {
Rectangle clip = new Rectangle(config.avatarSize, config.avatarSize);
clip.setArcWidth(config.avatarArcSize);
clip.setArcHeight(config.avatarArcSize);
speakerImage.setClip(clip);
}
return speakerImage;
}

/**
* Implementation of {@link Step.Factory} as Service implementation creating
* {@link ShowSchedule}.
Expand Down Expand Up @@ -288,5 +354,11 @@ public static class Config extends AbstractConfig {
public double sessionHeight = 200;
public boolean showTrackAvatar = true;
public boolean circularAvatar = true;
public boolean showTags = false;
public boolean compressedAvatars = true;
public int compressedAvatarsLimit = 4;
public int columns = 2;
public boolean autoSeparateRoomTypes = false;
public boolean showCompanyName = false;
}
}
30 changes: 25 additions & 5 deletions emoji/src/main/java/org/tweetwallfx/emoji/control/EmojiFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.util.List;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
Expand All @@ -41,11 +42,13 @@ public class EmojiFlow extends TextFlow {
private static final Logger LOG = LoggerFactory.getLogger(EmojiFlow.class);

private final ObjectProperty<String> textProperty = new SimpleObjectProperty<>();
private final int emojiFitWidthProperty = 12;
private final int emojiFitHeightProperty = 12;
private final SimpleDoubleProperty emojiFitWidthProperty = new SimpleDoubleProperty(12);
private final SimpleDoubleProperty emojiFitHeightProperty = new SimpleDoubleProperty(12);

public EmojiFlow() {
this.textProperty.addListener((o, old, text) -> updateContent(text));
this.emojiFitWidthProperty.addListener((o, old, newValue) -> updateContent(textProperty.get()));
this.emojiFitHeightProperty.addListener((o, old, newValue) -> updateContent(textProperty.get()));
}

public final String getText() {
Expand All @@ -56,7 +59,24 @@ public final void setText(String text) {
textProperty.set(text);
}

public final double getEmojiFitWidth() {
return emojiFitWidthProperty.get();
}

public final void setEmojiFitWidth(double fitWidth) {
emojiFitWidthProperty.set(fitWidth);
}

public final double getEmojiFitHeight() {
return emojiFitHeightProperty.get();
}

public final void setEmojiFitHeight(double fitHeight) {
emojiFitHeightProperty.set(fitHeight);
}

private void updateContent(String message) {
this.getChildren().clear();
List<Object> obs = Emojify.tokenizeStringToTextAndEmoji(message);
for (Object ob : obs) {
if (ob instanceof String s) {
Expand All @@ -75,15 +95,15 @@ private void updateContent(String message) {
private Text createTextNode(String text) {
Text textNode = new Text();
textNode.setText(text);
textNode.getStyleClass().add("tweetText");
textNode.getStyleClass().add("emojiFlow");
textNode.applyCss();
return textNode;
}

private ImageView createEmojiImageNode(Twemoji emoji) throws NullPointerException {
ImageView imageView = new ImageView();
imageView.setFitWidth(emojiFitWidthProperty);
imageView.setFitHeight(emojiFitHeightProperty);
imageView.setFitWidth(emojiFitWidthProperty.get());
imageView.setFitHeight(emojiFitHeightProperty.get());
imageView.setImage(new Image(EmojiImageCache.INSTANCE.get(emoji.hex()).getInputStream()));
return imageView;
}
Expand Down

0 comments on commit 0cc7172

Please sign in to comment.