TablePlugin defer table-row invalidation
This commit is contained in:
parent
620da87694
commit
2a43797023
@ -4,6 +4,9 @@
|
||||
* Add `Markwon.TextSetter` interface to be able to use PrecomputedText/PrecomputedTextCompat
|
||||
* Add `PrecomputedTextSetterCompat` and `compileOnly` dependency on `androidx.core:core`
|
||||
(clients must have this dependency in the classpath)
|
||||
* Add `requirePlugin(Class)` and `getPlugins` for `Markwon` instance
|
||||
* TablePlugin -> defer table invalidation (via `View.post`), so only one invalidation
|
||||
happens with each draw-call
|
||||
|
||||
# 4.0.2
|
||||
* Fix `JLatexMathPlugin` formula placeholder (cannot have line breaks) ([#149])
|
||||
|
@ -5,6 +5,7 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -15,6 +16,7 @@ import javax.inject.Inject;
|
||||
|
||||
import io.noties.debug.Debug;
|
||||
import io.noties.markwon.Markwon;
|
||||
import io.noties.markwon.utils.NoCopySpannableFactory;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
|
||||
@ -60,6 +62,9 @@ public class MainActivity extends Activity {
|
||||
|
||||
appBarRenderer.render(appBarState());
|
||||
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
textView.setSpannableFactory(NoCopySpannableFactory.getInstance());
|
||||
|
||||
markdownLoader.load(uri(), new MarkdownLoader.OnMarkdownTextLoaded() {
|
||||
@Override
|
||||
public void apply(final String text) {
|
||||
|
@ -18,9 +18,12 @@
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:breakStrategy="simple"
|
||||
android:hyphenationFrequency="none"
|
||||
android:lineSpacingExtra="2dip"
|
||||
android:textSize="16sp"
|
||||
tools:text="yo\nman" />
|
||||
tools:text="yo\nman"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
</ScrollView>
|
||||
|
||||
|
@ -9,6 +9,8 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.noties.markwon.core.CorePlugin;
|
||||
|
||||
/**
|
||||
@ -119,6 +121,19 @@ public abstract class Markwon {
|
||||
@Nullable
|
||||
public abstract <P extends MarkwonPlugin> P getPlugin(@NonNull Class<P> type);
|
||||
|
||||
/**
|
||||
* @since 4.1.0-SNAPSHOT
|
||||
*/
|
||||
@NonNull
|
||||
public abstract <P extends MarkwonPlugin> P requirePlugin(@NonNull Class<P> type);
|
||||
|
||||
/**
|
||||
* @return a list of registered {@link MarkwonPlugin}
|
||||
* @since 4.1.0-SNAPSHOT
|
||||
*/
|
||||
@NonNull
|
||||
public abstract List<? extends MarkwonPlugin> getPlugins();
|
||||
|
||||
/**
|
||||
* Interface to set text on a TextView. Primary goal is to give a way to use PrecomputedText
|
||||
* functionality
|
||||
|
@ -9,7 +9,9 @@ import androidx.annotation.Nullable;
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.Parser;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
@ -129,4 +131,21 @@ class MarkwonImpl extends Markwon {
|
||||
//noinspection unchecked
|
||||
return (P) out;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <P extends MarkwonPlugin> P requirePlugin(@NonNull Class<P> type) {
|
||||
final P plugin = getPlugin(type);
|
||||
if (plugin == null) {
|
||||
throw new IllegalStateException(String.format(Locale.US, "Requested plugin `%s` is not " +
|
||||
"registered with this Markwon instance", type.getName()));
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<? extends MarkwonPlugin> getPlugins() {
|
||||
return Collections.unmodifiableList(plugins);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import org.mockito.stubbing.Answer;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -23,6 +24,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@ -300,4 +302,64 @@ public class MarkwonImplTest {
|
||||
assertEquals(TextView.BufferType.EDITABLE, bufferTypeArgumentCaptor.getValue());
|
||||
assertNotNull(runnableArgumentCaptor.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void require_plugin_throws() {
|
||||
// if plugin is `required`, but it's not added -> an exception is thrown
|
||||
|
||||
final class NotPresent extends AbstractMarkwonPlugin {
|
||||
}
|
||||
|
||||
final List<MarkwonPlugin> plugins =
|
||||
Arrays.asList(mock(MarkwonPlugin.class), mock(MarkwonPlugin.class));
|
||||
|
||||
final MarkwonImpl impl = new MarkwonImpl(
|
||||
TextView.BufferType.SPANNABLE,
|
||||
null,
|
||||
mock(Parser.class),
|
||||
mock(MarkwonVisitor.class), plugins);
|
||||
|
||||
// should be returned
|
||||
assertNotNull(impl.requirePlugin(MarkwonPlugin.class));
|
||||
|
||||
try {
|
||||
impl.requirePlugin(NotPresent.class);
|
||||
fail();
|
||||
} catch (Throwable t) {
|
||||
assertTrue(t.getMessage(), t.getMessage().contains(NotPresent.class.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void plugins_unmodifiable() {
|
||||
// returned plugins list must not be modifiable
|
||||
|
||||
// modifiable list (created from Arrays.asList -> which returns non)
|
||||
final List<MarkwonPlugin> plugins = new ArrayList<>(
|
||||
Arrays.asList(mock(MarkwonPlugin.class), mock(MarkwonPlugin.class)));
|
||||
|
||||
// validate that list is modifiable
|
||||
plugins.add(mock(MarkwonPlugin.class));
|
||||
assertEquals(3, plugins.size());
|
||||
|
||||
final MarkwonImpl impl = new MarkwonImpl(
|
||||
TextView.BufferType.SPANNABLE,
|
||||
null,
|
||||
mock(Parser.class),
|
||||
mock(MarkwonVisitor.class),
|
||||
plugins);
|
||||
|
||||
final List<? extends MarkwonPlugin> list = impl.getPlugins();
|
||||
|
||||
// instance check (different list)
|
||||
//noinspection SimplifiableJUnitAssertion
|
||||
assertTrue(plugins != list);
|
||||
|
||||
try {
|
||||
list.add(null);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
assertTrue(e.getMessage(), true);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,9 +34,22 @@ abstract class TableRowsScheduler {
|
||||
}
|
||||
|
||||
final TableRowSpan.Invalidator invalidator = new TableRowSpan.Invalidator() {
|
||||
|
||||
// @since 4.1.0-SNAPSHOT
|
||||
// let's stack-up invalidation calls (so invalidation happens,
|
||||
// but not with each table-row-span draw call)
|
||||
final Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
view.setText(view.getText());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
view.setText(view.getText());
|
||||
// @since 4.1.0-SNAPSHOT post invalidation (combine multiple calls)
|
||||
view.removeCallbacks(runnable);
|
||||
view.post(runnable);
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user