diff --git a/src/main/java/seedu/foodrem/model/item/itemvalidators/ItemRemarksValidator.java b/src/main/java/seedu/foodrem/model/item/itemvalidators/ItemRemarksValidator.java index afcd5b40c8b..d1eaa175fbd 100644 --- a/src/main/java/seedu/foodrem/model/item/itemvalidators/ItemRemarksValidator.java +++ b/src/main/java/seedu/foodrem/model/item/itemvalidators/ItemRemarksValidator.java @@ -9,7 +9,8 @@ */ public class ItemRemarksValidator implements Validator { // Validation for remarks length - private static final int MAX_LENGTH = 1000; + // TODO: Ensure UG is standardised + private static final int MAX_LENGTH = 10000; private static final String MESSAGE_FOR_REMARKS_TOO_LONG = String.format("The item remark should not exceed %d characters", MAX_LENGTH); diff --git a/src/main/java/seedu/foodrem/model/tag/TagName.java b/src/main/java/seedu/foodrem/model/tag/TagName.java index d94bfd5d139..5777db12f52 100644 --- a/src/main/java/seedu/foodrem/model/tag/TagName.java +++ b/src/main/java/seedu/foodrem/model/tag/TagName.java @@ -10,6 +10,14 @@ * Guarantees: immutable; is valid as declared in {@link StringUtil#isValidString(String)} */ public class TagName { + private static final String MESSAGE_FOR_NAME_IS_BLANK = + "The tag name should not be blank."; + + // Validation for name length + private static final int MAX_LENGTH = 20; + private static final String MESSAGE_FOR_NAME_TOO_LONG = + String.format("The tag name should not exceed %d characters", MAX_LENGTH); + private final String fullName; /** @@ -19,6 +27,12 @@ public class TagName { */ public TagName(String name) { requireNonNull(name); + + boolean isNamePresent = !name.isBlank(); + checkArgument(isNamePresent, MESSAGE_FOR_NAME_IS_BLANK); + boolean isNameLengthLessThanEqualMaxLength = name.length() <= MAX_LENGTH; + checkArgument(isNameLengthLessThanEqualMaxLength, MESSAGE_FOR_NAME_TOO_LONG); + checkArgument(StringUtil.isValidString(name), StringUtil.getInvalidCharactersMessage("tag name")); fullName = name; } diff --git a/src/main/java/seedu/foodrem/ui/ItemCard.java b/src/main/java/seedu/foodrem/ui/ItemCard.java index 079a5242b86..e77da6c2874 100644 --- a/src/main/java/seedu/foodrem/ui/ItemCard.java +++ b/src/main/java/seedu/foodrem/ui/ItemCard.java @@ -1,9 +1,10 @@ package seedu.foodrem.ui; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; import javafx.fxml.FXML; -import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; @@ -11,11 +12,15 @@ import seedu.foodrem.model.item.Item; import seedu.foodrem.model.tag.Tag; import seedu.foodrem.views.ItemView; +import seedu.foodrem.views.TagView; /** * A UI component that displays information of a {@code Item}. */ public class ItemCard extends UiPart { + private static final int CHAR_LIMIT = 45; + private static final int SPACING_UNIT = 3; + /** * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. * As a consequence, UI elements' variable names cannot be set to such keywords @@ -49,14 +54,27 @@ public ItemCard(Item item, int displayedIndex) { quantity.setText(ItemView.buildItemQuantityAndUnitStringFrom(item)); quantity.setTextAlignment(TextAlignment.RIGHT); - item.getTagSet().stream().sorted(Comparator.comparing(Tag::getName)) - .forEach(tag -> tags.getChildren().add(buildTagNodeFrom(tag.getName()))); - } + final List tagList = new ArrayList<>(item.getTagSet()); + tagList.sort(Comparator.comparing(Tag::getName)); - private static Node buildTagNodeFrom(String tagName) { - final Label label = new Label(tagName); - label.getStyleClass().add("item-listview-tag"); - return label; + int currentLength = 0; + int currentIndex; + for (currentIndex = 0; currentIndex < tagList.size(); currentIndex++) { + Tag tag = tagList.get(currentIndex); + currentLength += tag.getName().length(); + if (currentLength > CHAR_LIMIT) { + break; + } + currentLength += SPACING_UNIT; // Account for padding between tags + tags.getChildren().add(TagView.from(tag, true)); + } + + final int size = item.getTagSet().size(); + if (size > currentIndex) { + final Label overflowLabel = new Label(String.format("+%d more...", size - currentIndex)); + overflowLabel.getStyleClass().add("tags-overflow-label"); + tags.getChildren().add(overflowLabel); + } } @Override diff --git a/src/main/java/seedu/foodrem/ui/ResultDisplay.java b/src/main/java/seedu/foodrem/ui/ResultDisplay.java index 2bf949a785b..676b2d1a1df 100644 --- a/src/main/java/seedu/foodrem/ui/ResultDisplay.java +++ b/src/main/java/seedu/foodrem/ui/ResultDisplay.java @@ -2,24 +2,30 @@ import javafx.fxml.FXML; import javafx.scene.Node; +import javafx.scene.control.ScrollPane; import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; /** * A UI for the status bar that is displayed at the header of the application. */ public class ResultDisplay extends UiPart { - @FXML private StackPane placeHolder; + @FXML private ScrollPane scrollPane; + @FXML private VBox pane; public ResultDisplay() { super("ResultDisplay.fxml"); } public void clear() { - placeHolder.getChildren().clear(); + pane.getChildren().clear(); } + /** + * Places a node into the scrollPane. + */ public void place(Node item) { - placeHolder.getChildren().add(item); + pane.getChildren().add(item); + scrollPane.setContent(pane); } } diff --git a/src/main/java/seedu/foodrem/views/ItemView.java b/src/main/java/seedu/foodrem/views/ItemView.java index 9c13449b10f..c6fea4b0c8b 100644 --- a/src/main/java/seedu/foodrem/views/ItemView.java +++ b/src/main/java/seedu/foodrem/views/ItemView.java @@ -4,10 +4,11 @@ import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.Separator; +import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import javafx.scene.text.TextAlignment; import seedu.foodrem.model.item.Item; /** @@ -25,45 +26,44 @@ private ItemView() {} // Prevents instantiation * @return the node to be displayed in the UI. */ public static Node from(Item item) { + // Name final Label name = new Label(item.getName().toString()); name.getStyleClass().add("item-detail-name"); name.prefWidth(Double.MAX_VALUE); + name.setWrapText(true); - // Name and tags at the top left - final HBox tags = new HBox(new Label("Tags: ")); + // Tags + final FlowPane tags = new FlowPane(new Label("Tags: ")); tags.getChildren().addAll(TagsView.from(item.getTagSet())); tags.setAlignment(Pos.CENTER_LEFT); - tags.setSpacing(SPACING_UNIT); + tags.setHgap(SPACING_UNIT); + tags.setVgap(SPACING_UNIT); - // Quantity and unit at the top right - final Label quantityLabel = new Label("Quantity\nRemaining:"); - quantityLabel.setTextAlignment(TextAlignment.RIGHT); - final Label quantityAndUnit = new Label(buildItemQuantityAndUnitStringFrom(item)); - quantityAndUnit.getStyleClass().add("item-detail-quantity"); - final VBox quantityBox = new VBox(quantityLabel, quantityAndUnit); - quantityBox.setAlignment(Pos.CENTER_RIGHT); + // Quantity/unit and price row + final Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + final HBox quantityAndPrice = new HBox( + new Label("Quantity: " + buildItemQuantityAndUnitStringFrom(item)), + spacer, + new Label("Price: $" + item.getPrice().toString())); - // Set up top half - final VBox col1 = new VBox(name, tags); - col1.setSpacing(SPACING_UNIT); - final HBox row1 = new HBox(col1, quantityBox); - HBox.setHgrow(col1, Priority.ALWAYS); - row1.setSpacing(SPACING_UNIT); - - final Label remarks = new Label( - String.valueOf(item.getRemarks()).isBlank() ? "-" : item.getRemarks().toString()); + final Label remarks = new Label(item.getRemarks().toString().isBlank() ? "-" : item.getRemarks().toString()); remarks.setWrapText(true); // Combine everything + final Separator linedSeparator = new Separator(); + linedSeparator.getStyleClass().add("lined-separator"); final VBox itemView = new VBox( - row1, - new Label("$" + item.getPrice().toString()), + name, + quantityAndPrice, new Separator(), new Label("Bought Date: " + buildBoughtDateStringFrom(item)), new Label("Expiry Date: " + buildExpiryDateStringFrom(item)), new Separator(), new Label("Remarks:"), - remarks); + remarks, + linedSeparator, + tags); itemView.setSpacing(SPACING_UNIT); return itemView; } diff --git a/src/main/java/seedu/foodrem/views/TagView.java b/src/main/java/seedu/foodrem/views/TagView.java index b27b804b8b0..f235951eaa9 100644 --- a/src/main/java/seedu/foodrem/views/TagView.java +++ b/src/main/java/seedu/foodrem/views/TagView.java @@ -9,6 +9,11 @@ * @author Richard Dominick */ public class TagView { + private static final String[] COLORS = new String[] { + "f3d3b5", "abdee6", "ffccb6", "ff9aa2", "c7ceea", "a5e3b3", "e2f0cb", "c5d7c0", "f3d1dc", "e6ede5", + "c8b4ba", "98afba", "70ae98" + }; + private TagView() {} // Prevents instantiation /** @@ -17,8 +22,24 @@ private TagView() {} // Prevents instantiation * @return the node to be displayed in the UI. */ public static Node from(Tag tag) { + return from(tag, false); + } + + /** + * Creates a new view of the given string. + * @param tag the tag to be displayed. + * @param isSmall whether the tag is small. + * @return the node to be displayed in the UI. + */ + public static Node from(Tag tag, boolean isSmall) { final Label label = new Label(tag.getName()); - label.getStyleClass().add("item-detail-tag"); + label.getStyleClass().add(isSmall ? "item-listview-tag" : "item-detail-tag"); + label.setStyle(String.format("-fx-background-color: #%s;", getColor(tag))); return label; } + + private static String getColor(Tag tag) { + final int index = tag.hashCode() % COLORS.length; + return COLORS[index]; + } } diff --git a/src/main/resources/view/FoodRem.css b/src/main/resources/view/FoodRem.css index 0124f2358bc..985d1f04c0a 100644 --- a/src/main/resources/view/FoodRem.css +++ b/src/main/resources/view/FoodRem.css @@ -105,6 +105,10 @@ -fx-font-weight: bold; // TODO } +.scroll-result-box { + -fx-background-color: transparent; +} + /* ============================ * * ItemCard * * ============================ */ @@ -129,3 +133,53 @@ .list-view .list-cell:odd { -fx-background-radius: 8; } + +.tags-overflow-label { + -fx-font-size: 8pt; + -fx-font-fill: gray; +} + +/* ============================ * + * ScrollBar * + * ============================ */ +.scroll-bar { + -fx-background-color: #ffffff; +} + +.scroll-bar .thumb { + -fx-background-color: #cccccc; + -fx-background-insets: 3; +} + +.scroll-bar .increment-button, +.scroll-bar .decrement-button { + -fx-background-color: transparent; + -fx-padding: 0 0 0 0; +} + +.scroll-bar .increment-arrow, +.scroll-bar .decrement-arrow { + -fx-shape: " "; +} + +.scroll-bar:vertical .increment-arrow, +.scroll-bar:vertical .decrement-arrow { + -fx-padding: 1 8 1 8; +} + +.scroll-bar:horizontal .increment-arrow, +.scroll-bar:horizontal .decrement-arrow { + -fx-padding: 8 1 8 1; +} + +/* ============================ * +* ResultDisplay * +* ============================ */ +.scroll-pane > .viewport { + -fx-background-color: #ffffff; + -fx-border-color: #ffffff; +} + +.scroll-pane { + -fx-background-color: transparent; +} diff --git a/src/main/resources/view/ItemListCard.fxml b/src/main/resources/view/ItemListCard.fxml index 5b652b1aae5..6bbaad7eb3b 100644 --- a/src/main/resources/view/ItemListCard.fxml +++ b/src/main/resources/view/ItemListCard.fxml @@ -24,6 +24,6 @@