WIP: Implement undo/redo commands
Signed-off-by: William Brawner <me@wbrawner.com>
This commit is contained in:
parent
0ff680d648
commit
dfd771adfe
8 changed files with 80 additions and 26 deletions
|
@ -24,6 +24,7 @@ import com.wbrawner.simplemarkdown.model.Readability
|
||||||
import com.wbrawner.simplemarkdown.utility.hideKeyboard
|
import com.wbrawner.simplemarkdown.utility.hideKeyboard
|
||||||
import com.wbrawner.simplemarkdown.utility.showKeyboard
|
import com.wbrawner.simplemarkdown.utility.showKeyboard
|
||||||
import com.wbrawner.simplemarkdown.view.ViewPagerPage
|
import com.wbrawner.simplemarkdown.view.ViewPagerPage
|
||||||
|
import com.wbrawner.simplemarkdown.viewmodel.EditorCommand
|
||||||
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
@ -94,8 +95,11 @@ class EditFragment : Fragment(), ViewPagerPage, CoroutineScope {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
markdownEditor?.setText(viewModel.markdownUpdates.value)
|
markdownEditor?.setText(viewModel.markdown.value)
|
||||||
viewModel.originalMarkdown.observe(viewLifecycleOwner, Observer {
|
viewModel.editorCommands.observe(viewLifecycleOwner, Observer {
|
||||||
|
when (it) {
|
||||||
|
is EditorCommand.Undo -> markdownEditor?.text?.
|
||||||
|
}
|
||||||
markdownEditor?.setText(it)
|
markdownEditor?.setText(it)
|
||||||
})
|
})
|
||||||
launch {
|
launch {
|
||||||
|
|
|
@ -75,7 +75,7 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||||
}
|
}
|
||||||
R.id.action_share -> {
|
R.id.action_share -> {
|
||||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, viewModel.markdownUpdates.value)
|
shareIntent.putExtra(Intent.EXTRA_TEXT, viewModel.markdown.value)
|
||||||
shareIntent.type = "text/plain"
|
shareIntent.type = "text/plain"
|
||||||
startActivity(Intent.createChooser(
|
startActivity(Intent.createChooser(
|
||||||
shareIntent,
|
shareIntent,
|
||||||
|
@ -223,7 +223,7 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun promptSaveOrDiscardChanges() {
|
private fun promptSaveOrDiscardChanges() {
|
||||||
if (viewModel.originalMarkdown.value == viewModel.markdownUpdates.value) {
|
if (viewModel.originalMarkdown.value == viewModel.markdown.value) {
|
||||||
viewModel.reset("Untitled.md")
|
viewModel.reset("Untitled.md")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,8 @@ class PreviewFragment : Fragment(), CoroutineScope {
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
updateWebContent(viewModel.markdownUpdates.value ?: "")
|
updateWebContent(viewModel.markdown.value ?: "")
|
||||||
viewModel.markdownUpdates.observe(this, Observer {
|
viewModel.markdown.observe(this, Observer {
|
||||||
updateWebContent(it)
|
updateWebContent(it)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,12 @@ import java.io.Reader
|
||||||
|
|
||||||
class MarkdownViewModel : ViewModel() {
|
class MarkdownViewModel : ViewModel() {
|
||||||
val fileName = MutableLiveData<String>("Untitled.md")
|
val fileName = MutableLiveData<String>("Untitled.md")
|
||||||
val markdownUpdates = MutableLiveData<String>()
|
val editorCommands = MutableLiveData<EditorCommand>()
|
||||||
val originalMarkdown = MutableLiveData<String>()
|
val markdown = MutableLiveData<String>()
|
||||||
val uri = MutableLiveData<Uri>()
|
val uri = MutableLiveData<Uri>()
|
||||||
|
|
||||||
fun updateMarkdown(markdown: String?) {
|
fun updateMarkdown(markdown: String?) {
|
||||||
this.markdownUpdates.postValue(markdown ?: "")
|
this.markdown.postValue(markdown ?: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun load(context: Context, uri: Uri?): Boolean {
|
suspend fun load(context: Context, uri: Uri?): Boolean {
|
||||||
|
@ -28,8 +28,8 @@ class MarkdownViewModel : ViewModel() {
|
||||||
val fileInput = FileInputStream(it.fileDescriptor)
|
val fileInput = FileInputStream(it.fileDescriptor)
|
||||||
val fileName = uri.getName(context)
|
val fileName = uri.getName(context)
|
||||||
val content = fileInput.reader().use(Reader::readText)
|
val content = fileInput.reader().use(Reader::readText)
|
||||||
originalMarkdown.postValue(content)
|
markdown.postValue(content)
|
||||||
markdownUpdates.postValue(content)
|
editorCommands.postValue(EditorCommand.Load(content))
|
||||||
this@MarkdownViewModel.fileName.postValue(fileName)
|
this@MarkdownViewModel.fileName.postValue(fileName)
|
||||||
this@MarkdownViewModel.uri.postValue(uri)
|
this@MarkdownViewModel.uri.postValue(uri)
|
||||||
true
|
true
|
||||||
|
@ -48,7 +48,7 @@ class MarkdownViewModel : ViewModel() {
|
||||||
context.contentResolver.openOutputStream(uri, "rwt")
|
context.contentResolver.openOutputStream(uri, "rwt")
|
||||||
?.writer()
|
?.writer()
|
||||||
?.use {
|
?.use {
|
||||||
it.write(markdownUpdates.value ?: "")
|
it.write(markdown.value ?: "")
|
||||||
}
|
}
|
||||||
?: return@withContext false
|
?: return@withContext false
|
||||||
this@MarkdownViewModel.fileName.postValue(fileName)
|
this@MarkdownViewModel.fileName.postValue(fileName)
|
||||||
|
@ -62,7 +62,23 @@ class MarkdownViewModel : ViewModel() {
|
||||||
|
|
||||||
fun reset(untitledFileName: String) {
|
fun reset(untitledFileName: String) {
|
||||||
fileName.postValue(untitledFileName)
|
fileName.postValue(untitledFileName)
|
||||||
originalMarkdown.postValue("")
|
editorCommands.postValue(EditorCommand.Load(""))
|
||||||
markdownUpdates.postValue("")
|
markdown.postValue("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun undo() {
|
||||||
|
editorCommands.postValue(EditorCommand.Undo())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun redo() {
|
||||||
|
editorCommands.postValue(EditorCommand.Redo())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class EditorCommand {
|
||||||
|
val consumed = false
|
||||||
|
|
||||||
|
class Undo: EditorCommand()
|
||||||
|
class Redo: EditorCommand()
|
||||||
|
class Load(val text: String): EditorCommand()
|
||||||
|
}
|
||||||
|
|
10
app/src/main/res/drawable/ic_redo.xml
Normal file
10
app/src/main/res/drawable/ic_redo.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M18.4,10.6C16.55,8.99 14.15,8 11.5,8c-4.65,0 -8.58,3.03 -9.96,7.22L3.9,16c1.05,-3.19 4.05,-5.5 7.6,-5.5 1.95,0 3.73,0.72 5.12,1.88L13,16h9V7l-3.6,3.6z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_undo.xml
Normal file
10
app/src/main/res/drawable/ic_undo.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12.5,8c-2.65,0 -5.05,0.99 -6.9,2.6L2,7v9h9l-3.62,-3.62c1.39,-1.16 3.16,-1.88 5.12,-1.88 3.54,0 6.55,2.31 7.6,5.5l2.37,-0.78C21.08,11.03 17.15,8 12.5,8z"/>
|
||||||
|
</vector>
|
|
@ -7,19 +7,31 @@
|
||||||
android:title="@string/action_share"
|
android:title="@string/action_share"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_new"
|
android:id="@+id/action_undo"
|
||||||
android:title="@string/action_new"
|
android:icon="@drawable/ic_undo"
|
||||||
app:showAsAction="never" />
|
android:title="@string/action_undo"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_load"
|
android:id="@+id/action_redo"
|
||||||
android:title="@string/action_open"
|
android:icon="@drawable/ic_redo"
|
||||||
app:showAsAction="never" />
|
android:title="@string/action_redo"
|
||||||
<item
|
app:showAsAction="ifRoom" />
|
||||||
android:id="@+id/action_save"
|
<group android:id="@+id/editGroup">
|
||||||
android:title="@string/action_save" />
|
<item
|
||||||
<item
|
android:id="@+id/action_new"
|
||||||
android:id="@+id/action_save_as"
|
android:title="@string/action_new"
|
||||||
android:title="@string/action_save_as" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_load"
|
||||||
|
android:title="@string/action_open"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_save"
|
||||||
|
android:title="@string/action_save" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_save_as"
|
||||||
|
android:title="@string/action_save_as" />
|
||||||
|
</group>
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_lock_swipe"
|
android:id="@+id/action_lock_swipe"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
|
|
@ -77,6 +77,8 @@
|
||||||
<string name="action_rate">Rate SimpleMarkdown</string>
|
<string name="action_rate">Rate SimpleMarkdown</string>
|
||||||
<string name="support_thank_you">Thank you so much for your support!</string>
|
<string name="support_thank_you">Thank you so much for your support!</string>
|
||||||
<string name="description_heart">Heart</string>
|
<string name="description_heart">Heart</string>
|
||||||
|
<string name="action_undo">Undo</string>
|
||||||
|
<string name="action_redo">Redo</string>
|
||||||
<string-array name="pref_entries_dark_mode">
|
<string-array name="pref_entries_dark_mode">
|
||||||
<item>@string/pref_value_light</item>
|
<item>@string/pref_value_light</item>
|
||||||
<item>@string/pref_value_dark</item>
|
<item>@string/pref_value_dark</item>
|
||||||
|
|
Loading…
Reference in a new issue