From 486c342f86ba4366f321db95e9977ba6891f0af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Thu, 4 Jun 2020 19:44:48 +0200 Subject: [PATCH] Add graphical toolbox, instead of text menu. --- .../parts/ProgramakerCustomBlockPart.java | 2 +- .../minicards/parts/buttons/RoundButton.java | 8 +- .../minicards/toolbox/PartShowcaseView.java | 95 +++++++++++ .../minicards/toolbox/PartsHolder.java | 147 +++++++++++++----- .../codigoparallevar/minicards/utils/Box.java | 20 +++ app/src/main/res/layout/toolbox_accordion.xml | 17 ++ 6 files changed, 241 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/com/codigoparallevar/minicards/toolbox/PartShowcaseView.java create mode 100644 app/src/main/java/com/codigoparallevar/minicards/utils/Box.java create mode 100644 app/src/main/res/layout/toolbox_accordion.xml diff --git a/app/src/main/java/com/codigoparallevar/minicards/parts/ProgramakerCustomBlockPart.java b/app/src/main/java/com/codigoparallevar/minicards/parts/ProgramakerCustomBlockPart.java index 91f55f2..8520260 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/parts/ProgramakerCustomBlockPart.java +++ b/app/src/main/java/com/codigoparallevar/minicards/parts/ProgramakerCustomBlockPart.java @@ -524,7 +524,7 @@ public class ProgramakerCustomBlockPart implements Part, ProgramakerSignalListen // Listen to signal ProgramakerApi api = _partGrid.getApi(); if (api == null) { - Log.e(LogTag, "Cannot listen to API (API not found)"); + Log.i(LogTag, "Cannot listen to API. API not found, probably on a showcase."); return; } diff --git a/app/src/main/java/com/codigoparallevar/minicards/parts/buttons/RoundButton.java b/app/src/main/java/com/codigoparallevar/minicards/parts/buttons/RoundButton.java index dd0e01a..86cdef8 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/parts/buttons/RoundButton.java +++ b/app/src/main/java/com/codigoparallevar/minicards/parts/buttons/RoundButton.java @@ -70,22 +70,22 @@ public class RoundButton implements Part { @Override public int get_left() { - return _xCenter - _outerRadius / 2; + return _xCenter - _outerRadius; } @Override public int get_right() { - return _xCenter + _outerRadius / 2; + return _xCenter + _outerRadius; } @Override public int get_top() { - return _yCenter - _outerRadius / 2; + return _yCenter - _outerRadius; } @Override public int get_bottom() { - return _yCenter + _outerRadius / 2; + return _yCenter + _outerRadius; } @Override diff --git a/app/src/main/java/com/codigoparallevar/minicards/toolbox/PartShowcaseView.java b/app/src/main/java/com/codigoparallevar/minicards/toolbox/PartShowcaseView.java new file mode 100644 index 0000000..5a6c59b --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/toolbox/PartShowcaseView.java @@ -0,0 +1,95 @@ +package com.codigoparallevar.minicards.toolbox; + +import android.content.Context; +import android.graphics.Canvas; +import android.view.View; + +import com.codigoparallevar.minicards.PartInstantiator; +import com.codigoparallevar.minicards.ScrolledCanvas; +import com.codigoparallevar.minicards.SignalListenerManager; +import com.codigoparallevar.minicards.types.Part; +import com.codigoparallevar.minicards.types.PartGrid; +import com.codigoparallevar.minicards.types.Selectable; +import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector; +import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector; +import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector; +import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector; +import com.codigoparallevar.minicards.types.functional.Tuple2; +import com.programaker.api.ProgramakerApi; + +class PartShowcaseView extends View implements PartGrid { + private final Context ctx; + private final PartInstantiator instantiator; + private final Part part; + private final int partWidth; + private Tuple2 center = new Tuple2<>(0, 0); + + public PartShowcaseView(Context ctx, PartInstantiator instantiator) { + super(ctx); + + this.ctx = ctx; + this.instantiator = instantiator; + this.part = instantiator.build(this); + this.part.pause(); + int height = this.part.get_bottom() - this.part.get_top(); + partWidth = this.part.get_right() - this.part.get_left(); + + this.setMinimumHeight(height * 2); + this.setMinimumWidth(partWidth); + } + + @Override + public void onDraw(Canvas canvas) { + int centerx = - Math.max(canvas.getWidth(), partWidth) / 2; + int centery = - canvas.getHeight() / 2; + this.center = new Tuple2<>(centerx, centery); + ScrolledCanvas scrolledCanvas = new ScrolledCanvas(canvas, getCenteredOn()); + this.part.draw(scrolledCanvas, true); + } + + // Most interactive functionalities are not implemented, as it's not needed for the showcase + @Override + public Selectable getPartOn(int x, int y) { + return null; + } + + @Override + public ProgramakerApi getApi() { + return null; + } + + @Override + public SignalListenerManager getListenerManager() { + return null; + } + + @Override + public SignalInputConnector getSignalInputConnectorOn(int x, int y) { + return null; + } + + @Override + public BooleanInputConnector getBooleanInputConnectorOn(int x, int y) { + return null; + } + + @Override + public AnyInputConnector getAnyInputConnectorOn(int x, int y) { + return null; + } + + @Override + public StringInputConnector getStringInputConnectorOn(int xEnd, int yEnd) { + return null; + } + + @Override + public Tuple2 getCenteredOn() { + return center; + } + @Override + + public void update() { + + } +} diff --git a/app/src/main/java/com/codigoparallevar/minicards/toolbox/PartsHolder.java b/app/src/main/java/com/codigoparallevar/minicards/toolbox/PartsHolder.java index 4d27846..d63bf56 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/toolbox/PartsHolder.java +++ b/app/src/main/java/com/codigoparallevar/minicards/toolbox/PartsHolder.java @@ -2,9 +2,16 @@ package com.codigoparallevar.minicards.toolbox; import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; -import androidx.appcompat.app.AlertDialog; +import android.graphics.Color; +import android.os.Build; import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import androidx.appcompat.app.AlertDialog; import com.codigoparallevar.minicards.CanvasView; import com.codigoparallevar.minicards.PartInstantiator; @@ -14,12 +21,13 @@ import com.codigoparallevar.minicards.parts.logic.Toggle; import com.codigoparallevar.minicards.parts.samples.ColorBox; import com.codigoparallevar.minicards.parts.strings.ConvertToString; import com.codigoparallevar.minicards.types.Part; +import com.codigoparallevar.minicards.types.functional.Consumer; import com.codigoparallevar.minicards.types.functional.Tuple2; +import com.codigoparallevar.minicards.utils.Box; import com.programaker.api.data.ProgramakerBridgeInfo; import com.programaker.api.data.ProgramakerCustomBlock; import com.programaker.api.data.api_results.ProgramakerBridgeCustomBlockResult; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -42,6 +50,7 @@ public class PartsHolder { add(new PartCategory("Testing", BuiltInParts)); }}; private Map bridgeInfoMap; + private final static String LogTag = "PartsHolder"; public PartsHolder(Context context) { this.context = context; @@ -49,61 +58,113 @@ public class PartsHolder { public void openAddPartModal(final CanvasView canvasView) { AlertDialog.Builder builder = new AlertDialog.Builder(context); - Map categoryOptions = getPartCategories(); - String[] categoryNames = categoryOptions.keySet().toArray(new String[0]); - builder.setTitle("Choose part category") - .setItems(categoryNames, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichCategory) { - if ((whichCategory >= 0) && (whichCategory < categoryNames.length)){ - Map parts = getPartTypes(categoryOptions.get(categoryNames[whichCategory])); - String[] partNames = parts.keySet().toArray(new String[0]); + // HACK : Nasty trick to make the accordion be able to close a dialog that still doesn't exist + Box openedDialog = new Box<>(); + Consumer onComplete = _void -> { + if (openedDialog.get() == null) { + Log.e(LogTag, "Expected dialog to be opened already"); + } + else { + openedDialog.get().cancel(); + } + }; - builder.setTitle("Choose part type") - .setItems(partNames, (dialog1, whichPart) -> { - if ((whichPart >= 0) && (whichPart < partNames.length)) { - String partName = partNames[whichPart]; + View categoryAccordion = generateCategoryAccordion(canvasView, onComplete); - Log.d("Minicards partsHolder", - "Spawning " + partName); - - PartInstantiator instantiator = parts.get(partName); - PartsHolder.this.runInstantiator(instantiator, canvasView); - } - }); - - Dialog dialog2 = builder.create(); - dialog2.show(); - } - } - }); + builder.setTitle("Choose part") + .setView(categoryAccordion); Dialog dialog = builder.create(); + openedDialog.set(dialog); dialog.show(); } - private void runInstantiator(PartInstantiator instantiator, CanvasView canvasView) { - Part part = instantiator.build(canvasView); - canvasView.addPart(part); - } + private View generateCategoryAccordion(final CanvasView canvasView, Consumer signalComplete) { + ScrollView view = new ScrollView(context); + LinearLayout layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setDividerPadding(10); + layout.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE); - private static Map getPartCategories() { - Map partTypes = new LinkedHashMap<>(Categories.size()); - for (int i = 0; i < Categories.size(); i++){ - partTypes.put(Categories.get(i).getName(), Categories.get(i)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + layout.setDividerDrawable(context.getDrawable(android.R.drawable.divider_horizontal_bright)); } - return partTypes; - } + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ); - private static Map getPartTypes(PartCategory categoryOption) { - HashMap partMap = new LinkedHashMap<>(); + for (int i = 0; i < Categories.size(); i++) { + PartCategory category = Categories.get(i); - for (Tuple2 part : categoryOption.getParts()) { - partMap.put(part.item1, part.item2); + LinearLayout categoryRow = new LinearLayout(context); + LinearLayout showcases = new LinearLayout(context); + + TextView categoryName = new TextView(context); //Creating Button + + // Add category name + categoryName.setId(i); //Setting Id for using in future + categoryName.setText(category.getName()); //Setting text + categoryName.setTextSize(15); //Text Size + categoryName.setPadding(50, 50, 50, 50); //padding + categoryName.setLayoutParams(params); //Setting Layout Params + categoryName.setTextColor(Color.parseColor("#000000")); //Text Color + categoryName.setBackgroundColor(Color.parseColor("#ffffff")); + categoryName.setGravity(Gravity.CENTER); + categoryName.setClickable(true); + categoryName.setOnClickListener(v -> { + if (showcases.getVisibility() == View.GONE) { + showcases.setVisibility(View.VISIBLE); + } + else { + showcases.setVisibility(View.GONE); + } + }); + + showcases.setVisibility(View.GONE); + + showcases.setBackgroundColor(Color.parseColor("#BBBBBB")); + showcases.setOrientation(LinearLayout.VERTICAL); + showcases.setDividerPadding(10); + showcases.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + showcases.setDividerDrawable(context.getDrawable(android.R.drawable.divider_horizontal_bright)); + } + + // Add showcases + int partHeights = 0; + for (Tuple2 entry : category.getParts()) { + PartShowcaseView showcase = new PartShowcaseView(context, entry.item2); + + showcase.setClickable(true); + showcase.setOnClickListener(v -> { + Part part = entry.item2.build(canvasView); + canvasView.addPart(part); + try { + signalComplete.apply(null); + } catch (Exception e) { + e.printStackTrace(); + } + }); + partHeights += showcase.getHeight(); + + showcases.addView(showcase); + } + showcases.setMinimumHeight(partHeights); + + // Tie everything + categoryRow.setOrientation(LinearLayout.VERTICAL); + categoryRow.addView(categoryName); + showcases.setOrientation(LinearLayout.VERTICAL); + categoryRow.addView(showcases); + + layout.addView(categoryRow); } - return partMap; + view.addView(layout); + return view; } public void addCustomBlocks(List bridgeInfo, List customBlocks) { diff --git a/app/src/main/java/com/codigoparallevar/minicards/utils/Box.java b/app/src/main/java/com/codigoparallevar/minicards/utils/Box.java new file mode 100644 index 0000000..e827369 --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/utils/Box.java @@ -0,0 +1,20 @@ +package com.codigoparallevar.minicards.utils; + +public class Box { + private T val = null; + + public Box() { + } + + public Box(T val) { + this.val = val; + } + + public T get() { + return val; + } + + public void set(T val) { + this.val = val; + } +} diff --git a/app/src/main/res/layout/toolbox_accordion.xml b/app/src/main/res/layout/toolbox_accordion.xml new file mode 100644 index 0000000..2f433d4 --- /dev/null +++ b/app/src/main/res/layout/toolbox_accordion.xml @@ -0,0 +1,17 @@ + + + + +