diff --git a/opentasks/src/main/java/org/dmfs/tasks/model/DescriptionItem.java b/opentasks/src/main/java/org/dmfs/tasks/model/DescriptionItem.java index 10019dbe..3a8b1924 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/model/DescriptionItem.java +++ b/opentasks/src/main/java/org/dmfs/tasks/model/DescriptionItem.java @@ -27,13 +27,15 @@ public final class DescriptionItem public boolean checkbox; public boolean checked; public String text; + public int level; - public DescriptionItem(boolean checkbox, boolean checked, String text) + public DescriptionItem(boolean checkbox, boolean checked, String text, int level) { this.checkbox = checkbox; this.checked = checked; this.text = text; + this.level = level; } @@ -43,13 +45,14 @@ public boolean equals(@Nullable Object obj) return obj instanceof DescriptionItem && ((DescriptionItem) obj).checkbox == checkbox && ((DescriptionItem) obj).checked == checked - && ((DescriptionItem) obj).text.equals(text); + && ((DescriptionItem) obj).text.equals(text) + && ((DescriptionItem) obj).level == level; } @Override public int hashCode() { - return text.hashCode() * 31 + (checkbox ? 1 : 0) + (checked ? 2 : 0); + return text.hashCode() * 31 + (checkbox ? 1 : 0) + (checked ? 2 : 0) + level * 17; } } diff --git a/opentasks/src/main/java/org/dmfs/tasks/model/adapters/DescriptionFieldAdapter.java b/opentasks/src/main/java/org/dmfs/tasks/model/adapters/DescriptionFieldAdapter.java index 6de89a07..bae4de19 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/model/adapters/DescriptionFieldAdapter.java +++ b/opentasks/src/main/java/org/dmfs/tasks/model/adapters/DescriptionFieldAdapter.java @@ -36,7 +36,7 @@ */ public final class DescriptionFieldAdapter extends FieldAdapter> { - private final static Pattern CHECKMARK_PATTERN = Pattern.compile("([-*] )?\\[([xX ])\\](.*)"); + private final static Pattern CHECKMARK_PATTERN = Pattern.compile("(\\s*)([-*] )?\\[([xX ])\\](.*)"); /** * The field name this adapter uses to store the values. @@ -169,6 +169,7 @@ private static List parseDescription(String description) StringBuilder currentParagraph = new StringBuilder(); boolean currentHasCheckedMark = false; boolean currentIsChecked = false; + int currentLevel = 0; for (String line : description.split("\n")) { matcher.reset(line); @@ -179,12 +180,13 @@ private static List parseDescription(String description) if (currentParagraph.length() > 0) { result.add(new DescriptionItem(currentHasCheckedMark, currentIsChecked, - currentHasCheckedMark ? currentParagraph.toString().trim() : currentParagraph.toString())); + currentHasCheckedMark ? currentParagraph.toString().trim() : currentParagraph.toString(), currentLevel)); } currentHasCheckedMark = true; - currentIsChecked = "x".equals(matcher.group(2).toLowerCase()); + currentIsChecked = "x".equals(matcher.group(3).toLowerCase()); currentParagraph.setLength(0); - currentParagraph.append(matcher.group(3)); + currentParagraph.append(matcher.group(4)); + currentLevel = (matcher.group(1).length() + 1) / 2; //FIXME: Make the levels not static } else { @@ -194,7 +196,7 @@ private static List parseDescription(String description) if (currentParagraph.length() > 0) { // close last paragraph - result.add(new DescriptionItem(currentHasCheckedMark, currentIsChecked, currentParagraph.toString().trim())); + result.add(new DescriptionItem(currentHasCheckedMark, currentIsChecked, currentParagraph.toString().trim(), currentLevel)); } currentHasCheckedMark = false; currentParagraph.setLength(0); @@ -211,7 +213,7 @@ private static List parseDescription(String description) if (currentHasCheckedMark || currentParagraph.length() > 0) { result.add(new DescriptionItem(currentHasCheckedMark, currentIsChecked, - currentHasCheckedMark ? currentParagraph.toString().trim() : currentParagraph.toString())); + currentHasCheckedMark ? currentParagraph.toString().trim() : currentParagraph.toString(), currentLevel)); } return result; } @@ -235,6 +237,10 @@ private static void serializeDescription(StringBuilder sb, List { sb.append('\n'); } + for (int i = 0; i < item.level; i++) + { + sb.append(" "); + } if (item.checkbox) { sb.append(item.checked ? "- [x] " : "- [ ] "); diff --git a/opentasks/src/main/java/org/dmfs/tasks/widget/DescriptionFieldView.java b/opentasks/src/main/java/org/dmfs/tasks/widget/DescriptionFieldView.java index eca15c9d..fd438a2e 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/widget/DescriptionFieldView.java +++ b/opentasks/src/main/java/org/dmfs/tasks/widget/DescriptionFieldView.java @@ -62,6 +62,7 @@ import java.util.List; +import androidx.appcompat.widget.AppCompatImageView; import androidx.core.view.ViewCompat; import static org.dmfs.jems.optional.elementary.Absent.absent; @@ -194,6 +195,7 @@ private void updateCheckList(List list) if (itemView == null || itemView.getId() != R.id.checklist_element) { itemView = createItemView(); + mContainer.addDragView(itemView, itemView.findViewById(R.id.drag_handle), mContainer.getChildCount() - 1); } @@ -249,9 +251,28 @@ private View createItemView() private void bindItemView(final View itemView, final DescriptionItem item) { + // set the left indend button listener + AppCompatImageView indend_left = itemView.findViewById(R.id.indend_left); + indend_left.setOnClickListener((view -> { + if (item.level > 0) { + item.level -= 1; + bindItemView(itemView, item); + } + })); + AppCompatImageView indend_right = itemView.findViewById(R.id.indend_right); + indend_right.setOnClickListener((view -> { + item.level += 1; + bindItemView(itemView, item); + })); + // set the checkbox status CheckBox checkbox = itemView.findViewById(android.R.id.checkbox); + // set the left margin for hierarchical lists + MarginLayoutParams lp = (MarginLayoutParams) checkbox.getLayoutParams(); + lp.leftMargin = checkbox.getPaddingLeft() * 10 * item.level; + checkbox.setLayoutParams(lp); + // make sure we don't receive our own updates checkbox.setOnCheckedChangeListener(null); checkbox.setChecked(item.checked && item.checkbox); @@ -463,7 +484,7 @@ private void insertItem(boolean withCheckBox, int pos, String initialText) mContainer.clearFocus(); // create a new empty item - DescriptionItem item = new DescriptionItem(withCheckBox, false, initialText); + DescriptionItem item = new DescriptionItem(withCheckBox, false, initialText, 0); mCurrentValue.add(pos, item); View newItem = createItemView(); bindItemView(newItem, item); @@ -529,7 +550,7 @@ private void setupActionView(DescriptionItem item) if (lines.length == 1) { - DescriptionItem newItem = new DescriptionItem(true, item.checked, item.text); + DescriptionItem newItem = new DescriptionItem(true, item.checked, item.text, 0); mCurrentValue.add(idx, newItem); new Animated(mContainer.getChildAt(origidx), v -> (ViewGroup) v).process(v -> bindItemView(v, newItem)); setupActionView(newItem); @@ -538,7 +559,7 @@ private void setupActionView(DescriptionItem item) { for (String i : lines) { - DescriptionItem newItem = new DescriptionItem(true, false, i); + DescriptionItem newItem = new DescriptionItem(true, false, i, 0); mCurrentValue.add(idx, newItem); if (idx == origidx) { @@ -557,7 +578,7 @@ private void setupActionView(DescriptionItem item) } else { - DescriptionItem newItem = new DescriptionItem(false, item.checked, item.text); + DescriptionItem newItem = new DescriptionItem(false, item.checked, item.text, item.level); mCurrentValue.add(idx, newItem); if (idx == 0 || mCurrentValue.get(idx - 1).checkbox) { diff --git a/opentasks/src/main/res/drawable/ic_left_arrow.xml b/opentasks/src/main/res/drawable/ic_left_arrow.xml new file mode 100644 index 00000000..5837b801 --- /dev/null +++ b/opentasks/src/main/res/drawable/ic_left_arrow.xml @@ -0,0 +1,4 @@ + + + diff --git a/opentasks/src/main/res/drawable/ic_right_arrow.xml b/opentasks/src/main/res/drawable/ic_right_arrow.xml new file mode 100644 index 00000000..3163ed81 --- /dev/null +++ b/opentasks/src/main/res/drawable/ic_right_arrow.xml @@ -0,0 +1,4 @@ + + + diff --git a/opentasks/src/main/res/layout/description_field_view_element.xml b/opentasks/src/main/res/layout/description_field_view_element.xml index 1c155543..7cf4105d 100644 --- a/opentasks/src/main/res/layout/description_field_view_element.xml +++ b/opentasks/src/main/res/layout/description_field_view_element.xml @@ -1,5 +1,5 @@ - + android:animateLayoutChanges="true" + android:orientation="horizontal"> - - + android:padding="4dp"/> + android:textSize="16sp"/> + + + + - - \ No newline at end of file + android:padding="4dp" + android:src="@drawable/ic_drag_indicator_24px"/> + \ No newline at end of file