Ver código fonte

Merge branches 'refactor/hide', 'feat/image-file' and 'bugfix/instruction-size'

* refactor/hide:
  Hide prep panel in lock mode when no prep*.
  Hide rest panel in lock mode when no rest

* feat/image-file:
  Fix selection sustem
  Allow the applying of an image.
  Start designing ability to save photos of images.

* bugfix/instruction-size:
  Fix instruction size cutting off data.
Sam Jaffe 4 anos atrás
pai
commit
9e4722af37

+ 6 - 3
README.md

@@ -12,11 +12,14 @@ A java program for the purpose of storing and categorizing various recipes.
 - [x] Allow specifying instructions on every step
 - [x] Allow user to lock a recipe, to view it as complete instead of in-edit
 - [x] Change number of servings from default
-- [ ] Investigate blank screen on load
-- [ ] Ensure that instructions are not cut off in lock-view
-- [ ] Support images
+- [x] Hide Prep/Rest in lock mode when empty
+- [x] Ensure that instructions are not cut off in lock-view
+- [x] Support images
 - [ ] Ingredient/Tag searching for recipes
 
+### Bugs
+- [ ] Summary for ingredients not being generated automatically...
+
 ## 0.2.0 Features
 
 - [ ] Nutrition calculator

+ 55 - 0
src/main/lombok/org/leumasjaffe/recipe/controller/SetImageAction.java

@@ -0,0 +1,55 @@
+package org.leumasjaffe.recipe.controller;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import org.leumasjaffe.recipe.model.RecipeCard;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.FieldDefaults;
+import lombok.experimental.NonFinal;
+
+@RequiredArgsConstructor
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+public class SetImageAction extends MouseAdapter {
+	private static class ImageFileFilter extends FileFilter {
+		@Override
+		public boolean accept(File f) {
+			if (f.isDirectory()) { return true; }
+			final String ext = Stream.of(f.getAbsolutePath())
+					.filter(p -> p.contains("."))
+					.map(p -> p.substring(p.lastIndexOf(".") + 1))
+					.findFirst().orElse("");
+			return Stream.of("png").anyMatch(s -> s.equalsIgnoreCase(ext));
+		}
+
+		@Override
+		public String getDescription() { return "Only image files"; }
+	}
+
+	Consumer<String> setPhoto;
+	@NonFinal @Setter RecipeCard model = null;
+	
+	@Override
+	public void mousePressed(MouseEvent e) {
+		if (model == null || e.getClickCount() != 2) { return; }
+		
+		final JFileChooser chooser = new JFileChooser("./");
+		chooser.setFileFilter(new ImageFileFilter());
+		if (chooser.showOpenDialog(e.getComponent()) != JFileChooser.APPROVE_OPTION) {
+			return;
+		}
+		
+		final String path = chooser.getSelectedFile().getAbsolutePath();
+		model.setPhoto(path);
+		setPhoto.accept(path);
+	}
+}

+ 5 - 3
src/main/lombok/org/leumasjaffe/recipe/model/RecipeCard.java

@@ -7,8 +7,6 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Stream;
 
-import javax.swing.ImageIcon;
-
 import org.leumasjaffe.observer.Observable;
 
 import lombok.Data;
@@ -21,9 +19,13 @@ public class RecipeCard extends Observable.Instance implements CompoundRecipeCom
 	int servings = 1;
 	Set<String> tags = new HashSet<>();
 	// TODO: Nutrition information
-	Optional<ImageIcon> photo = Optional.empty(); // TODO JSONIZATION	
+	Optional<String> photo = Optional.empty(); // TODO JSONIZATION	
 	List<Element> elements = new ArrayList<>();
 	
+	public void setPhoto(final String photo) {
+		this.photo = Optional.of(photo);
+	}
+	
 	@Override
 	public Stream<Element> getComponents() {
 		return getElements().stream();

+ 25 - 10
src/main/lombok/org/leumasjaffe/recipe/view/ImagePanel.java

@@ -2,6 +2,7 @@ package org.leumasjaffe.recipe.view;
 
 import java.awt.Dimension;
 import java.awt.Graphics;
+import java.awt.Image;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
@@ -10,12 +11,13 @@ import javax.imageio.ImageIO;
 import javax.swing.JPanel;
 
 import lombok.Getter;
+import lombok.NonNull;
 import lombok.Synchronized;
 
 @SuppressWarnings("serial")
 public class ImagePanel extends JPanel {
 	private static final @Getter(lazy=true) BufferedImage placeholder = getDefaultImage();
-    private BufferedImage image;
+    private Image image;
 
     public ImagePanel() {
     	image = getPlaceholder();
@@ -23,15 +25,28 @@ public class ImagePanel extends JPanel {
     	setPreferredSize(new Dimension(200, 200));
     }
     
-    public ImagePanel(String imagePath) {
-       try {                
-          image = ImageIO.read(new File(imagePath));
-       } catch (IOException ex) {
-          image = getPlaceholder();
-       }
-       setMinimumSize(new Dimension(200, 200));
-       setPreferredSize(new Dimension(200, 200));
-   }
+    public ImagePanel(final @NonNull String path) {
+    	setImage(path);
+    	setMinimumSize(new Dimension(200, 200));
+    	setPreferredSize(new Dimension(200, 200));
+    }
+    
+    public void setImage(final String path) {
+    	if (path == null || !setImageImpl(path)) {
+    		image = getPlaceholder();
+    		repaint();
+    	}
+    }
+
+    private boolean setImageImpl(final @NonNull String path) {
+    	try {                
+    		image = ImageIO.read(new File(path)).getScaledInstance(200, 200, Image.SCALE_SMOOTH);
+    		repaint();
+    		return true;
+    	} catch (IOException ex) {
+    		return false;
+    	}
+    }
 
     @Synchronized
     private static BufferedImage getDefaultImage() {

+ 2 - 0
src/main/lombok/org/leumasjaffe/recipe/view/PreparationPanel.java

@@ -113,6 +113,8 @@ public class PreparationPanel extends JPanel {
 	@Override
 	public void setEnabled(boolean enabled) {
 		super.setEnabled(enabled);
+		// Kind of a kludge unless I add the preparation model as a member
+		setVisible(enabled || !panelDuration.txtTime.getText().equals("0 s"));
 		panelDuration.txtTime.setEditable(enabled);
 	}
 	

+ 6 - 5
src/main/lombok/org/leumasjaffe/recipe/view/RestPanel.java

@@ -66,8 +66,8 @@ public class RestPanel extends JPanel {
 		add(panelUpTo, gbc_panelUpTo);
 
 		jcbLocation.addItemListener(e -> {
-			panelDuration.txtTime.setEditable(!isResting());
-			panelUpTo.txtTime.setEditable(!isResting());
+			panelDuration.txtTime.setEditable(isResting());
+			panelUpTo.txtTime.setEditable(isResting());
 			this.model.setWhere(Rest.Where.class.cast(e.getItem()));
 		});
 		durationController = ObservableController.from(panelDuration.txtTime,
@@ -94,9 +94,10 @@ public class RestPanel extends JPanel {
 	@Override
 	public void setEnabled(boolean enabled) {
 		super.setEnabled(enabled);
+		setVisible(enabled || isResting());
 		jcbLocation.setEnabled(enabled);
-		panelDuration.txtTime.setEditable(enabled && !isResting());
-		panelUpTo.txtTime.setEditable(enabled && !isResting());
+		panelDuration.txtTime.setEditable(enabled && isResting());
+		panelUpTo.txtTime.setEditable(enabled && isResting());
 	}
 	
 	@Override
@@ -106,7 +107,7 @@ public class RestPanel extends JPanel {
 	}
 
 	private boolean isResting() {
-		return jcbLocation.getSelectedItem().equals(Rest.Where.NONE);
+		return !jcbLocation.getSelectedItem().equals(Rest.Where.NONE);
 	}
 
 }

+ 10 - 6
src/main/lombok/org/leumasjaffe/recipe/view/StepPanel.java

@@ -1,6 +1,7 @@
 package org.leumasjaffe.recipe.view;
 
 import javax.swing.JPanel;
+import javax.swing.JTextArea;
 import javax.swing.event.DocumentListener;
 
 import org.leumasjaffe.observer.ForwardingObservableListener;
@@ -23,13 +24,10 @@ import java.awt.Insets;
 import java.awt.event.FocusListener;
 
 import javax.swing.JLabel;
-import javax.swing.JTextPane;
 import java.awt.Component;
 import javax.swing.Box;
 import javax.swing.JFormattedTextField;
 
-import java.awt.Dimension;
-
 @SuppressWarnings("serial")
 @FieldDefaults(level=AccessLevel.PRIVATE)
 public class StepPanel extends JPanel implements AutoGrowPanel.ChildComponent {
@@ -38,10 +36,13 @@ public class StepPanel extends JPanel implements AutoGrowPanel.ChildComponent {
 	ObservableListener<JFormattedTextField, Step> durationController;
 
 	JLabel lblIndex;
-	@Getter(AccessLevel.PACKAGE) JTextPane txtpnInstructions;
+	@Getter(AccessLevel.PACKAGE) JTextArea txtpnInstructions;
 	AutoGrowPanel<IngredientPanel, Ingredient> panelIngredients;
 	DurationPanel panelDuration;
 		
+	/**
+	 * @wbp.parser.constructor
+	 */
 	public StepPanel(final ScaleFactor scale) {
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		gridBagLayout.columnWidths = new int[]{0, 0, 0};
@@ -94,8 +95,11 @@ public class StepPanel extends JPanel implements AutoGrowPanel.ChildComponent {
 		gbc_panelIngredients.gridy = 1;
 		panelLeft.add(panelIngredients, gbc_panelIngredients);
 		
-		txtpnInstructions = new JTextPane();
-		txtpnInstructions.setPreferredSize(new Dimension(200, 30));
+		// 0 rows -> automatic resizing
+		// 20 columns -> long enough for 44 characters, apparently
+		txtpnInstructions = new JTextArea(0, 20);
+		txtpnInstructions.setLineWrap(true);
+		txtpnInstructions.setWrapStyleWord(true);
 		GridBagConstraints gbc_txtpnInstructions = new GridBagConstraints();
 		gbc_txtpnInstructions.fill = GridBagConstraints.BOTH;
 		gbc_txtpnInstructions.gridx = 1;

+ 10 - 1
src/main/lombok/org/leumasjaffe/recipe/view/summary/SummaryPanel.java

@@ -19,6 +19,7 @@ import org.jdesktop.swingx.VerticalLayout;
 import org.leumasjaffe.observer.ObservableListener;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.controller.ReplaceChildrenAction;
+import org.leumasjaffe.recipe.controller.SetImageAction;
 import org.leumasjaffe.recipe.controller.SpinnerBinding;
 import org.leumasjaffe.recipe.controller.TextBinding;
 import org.leumasjaffe.recipe.model.Element;
@@ -36,6 +37,7 @@ import javax.swing.JSpinner;
 @FieldDefaults(level=AccessLevel.PRIVATE)
 public class SummaryPanel extends JPanel {
 	ReplaceChildrenAction<RecipeCard, Element> controller;
+	SetImageAction setImage;
 	TextBinding<RecipeCard> titleBinding;
 	SpinnerBinding<RecipeCard, Integer> servingsBinding;
 	SpinnerBinding<ScaleFactor, Integer> servingsToMakeBinding;
@@ -43,6 +45,7 @@ public class SummaryPanel extends JPanel {
 	ObservableListener<CollatedDurationPanel, RecipeCard> durationListener;
 	ObservableListener<JPanel, RecipeCard> childListener;
 	
+	ImagePanel panelPhoto;
 	JTextField txtTitle;
 	JTextArea txaDescription;
 	TagInputPanel panelTags;
@@ -151,7 +154,7 @@ public class SummaryPanel extends JPanel {
 		gbl_panel.rowWeights = new double[]{0.0, 0.0, 1.0, Double.MIN_VALUE};
 		panel.setLayout(gbl_panel);
 		
-		JPanel panelPhoto = new ImagePanel();
+		panelPhoto = new ImagePanel();
 		GridBagConstraints gbc_panelPhoto = new GridBagConstraints();
 		gbc_panelPhoto.fill = GridBagConstraints.BOTH;
 		gbc_panelPhoto.insets = new Insets(0, 0, 5, 0);
@@ -179,6 +182,9 @@ public class SummaryPanel extends JPanel {
 		gbc_txaDescription.gridy = 2;
 		panel.add(txaDescription, gbc_txaDescription);
 		
+		setImage = new SetImageAction(panelPhoto::setImage);
+		panelPhoto.addMouseListener(setImage);
+
 		titleBinding = new TextBinding<>(txtTitle,
 				RecipeCard::getTitle, RecipeCard::setTitle);
 		
@@ -209,6 +215,9 @@ public class SummaryPanel extends JPanel {
 	}
 	
 	public void setModel(final RecipeCard card) {
+		// We need to always update the image
+		panelPhoto.setImage(card.getPhoto().orElse(null));
+		setImage.setModel(card);
 		panelTags.setModel(card.getTags());
 		titleBinding.setModel(card);
 		servingsBinding.setModel(card);