6.7.0 - Alpha21 - 修复代码编辑器可能导致未保存内容被重置的问题 (试修)
This commit is contained in:
@@ -110,7 +110,32 @@ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyAc
|
||||
EditorMenu(editorView, readOnly).also {
|
||||
mEditorMenu = it
|
||||
}
|
||||
StableDraftFileHelper(this, editorView.uri?.path).also {
|
||||
|
||||
// Restore draft as early as possible to avoid being overwritten by async file loading.
|
||||
// We intentionally do this in onCreate(), not in onRestoreInstanceState(),
|
||||
// because handleIntent() may start async loading and call setInitialText() later.
|
||||
//
|
||||
// zh-CN:
|
||||
// 尽可能早地恢复草稿, 避免被异步文件加载覆盖.
|
||||
// 我们刻意在 onCreate() 中恢复, 而不是在 onRestoreInstanceState() 中恢复,
|
||||
// 因为 handleIntent() 可能启动异步加载并在稍后调用 setInitialText().
|
||||
savedInstanceState?.getString("text")?.let { draftText ->
|
||||
mEditorView.restoreDraftTextForThisSession(draftText)
|
||||
} ?: savedInstanceState?.getString("path")?.let { path ->
|
||||
Observable.just(path)
|
||||
.observeOn(Schedulers.io())
|
||||
.map { PFiles.read(it) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ draftText ->
|
||||
mEditorView.restoreDraftTextForThisSession(draftText)
|
||||
}, Throwable::printStackTrace)
|
||||
}
|
||||
|
||||
// Use a stable key from intent instead of editorView.uri (which is not set yet here).
|
||||
// zh-CN: 使用 intent 中的稳定 key, 避免此处 editorView.uri 尚未赋值导致 key 为 null.
|
||||
val draftKeyPath = intent.getStringExtra(EditorView.EXTRA_PATH) ?: intent.data?.path
|
||||
|
||||
StableDraftFileHelper(this, draftKeyPath).also {
|
||||
draftFileHelper = it
|
||||
}
|
||||
(intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0).also {
|
||||
@@ -403,9 +428,15 @@ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyAc
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
// Save draft when UI indicates "needs save".
|
||||
// zh-CN: 当 UI 表示 "需要保存" 时保存草稿.
|
||||
if (!mEditorView.saveStickyDirty) {
|
||||
|
||||
// Save draft when content actually differs from baseline OR UI indicates "needs save".
|
||||
// This makes state restore robust against any sticky/menu state glitches.
|
||||
//
|
||||
// zh-CN:
|
||||
// 当内容相对基线确实发生变化, 或 UI 表示 "需要保存" 时, 保存草稿.
|
||||
// 这能使状态恢复不再受 sticky/menu 状态偶发异常的影响.
|
||||
val needDraft = mEditorView.isTextChanged || mEditorView.saveStickyDirty
|
||||
if (!needDraft) {
|
||||
super.onSaveInstanceState(outState)
|
||||
return
|
||||
}
|
||||
@@ -431,24 +462,10 @@ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyAc
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mEditorView.syncPrimaryMenuState()
|
||||
runCatching { mEditorView.refreshSymbolsBar() }
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
savedInstanceState.getString("text")?.let {
|
||||
mEditorView.setRestoredText(it)
|
||||
return
|
||||
}
|
||||
savedInstanceState.getString("path")?.let { path ->
|
||||
Observable.just(path)
|
||||
.observeOn(Schedulers.io())
|
||||
.map { PFiles.read(it) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ mEditorView.editor.text = it }, Throwable::printStackTrace)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addRequestPermissionsCallback(callback: OnRequestPermissionsResultCallback) {
|
||||
mRequestPermissionCallbacks.addCallback(callback)
|
||||
}
|
||||
|
||||
@@ -158,6 +158,15 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
|
||||
@Volatile
|
||||
private var mHadDirectEditSinceSave: Boolean = false
|
||||
|
||||
// Whether a draft has been restored in this session.
|
||||
// If true, subsequent async file-load setInitialText() must NOT overwrite the restored draft.
|
||||
//
|
||||
// zh-CN:
|
||||
// 本次会话是否已恢复草稿.
|
||||
// 若为 true, 则后续异步文件加载触发的 setInitialText() 不得覆盖已恢复的草稿.
|
||||
@Volatile
|
||||
private var mDraftRestoredInThisSession: Boolean = false
|
||||
|
||||
// Guard flag to mark text changes caused by undo/redo button actions.
|
||||
// This is used to distinguish direct edits from history navigation.
|
||||
//
|
||||
@@ -374,8 +383,20 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
|
||||
// Initialize menu state early to avoid "first render" flicker.
|
||||
// zh-CN: 尽早初始化菜单状态, 避免首次渲染闪烁.
|
||||
mEditorLoading = true
|
||||
saveStickyDirty = false
|
||||
mHadDirectEditSinceSave = false
|
||||
|
||||
// Do NOT reset dirty flags if we already restored a draft in this session.
|
||||
// Otherwise, the editor will keep the draft text but lose "needs save" state,
|
||||
// causing Save button to turn off and exit-confirm not to show.
|
||||
//
|
||||
// zh-CN:
|
||||
// 若本会话已恢复草稿, 则不要重置脏标记.
|
||||
// 否则会出现草稿文本仍在但 "需要保存" 状态丢失,
|
||||
// 导致保存按钮熄灭且退出不提示保存.
|
||||
if (!mDraftRestoredInThisSession) {
|
||||
saveStickyDirty = false
|
||||
mHadDirectEditSinceSave = false
|
||||
}
|
||||
|
||||
syncPrimaryMenuState()
|
||||
|
||||
val name = intent.getStringExtra(EXTRA_NAME)
|
||||
@@ -418,6 +439,49 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
|
||||
editor.text = text
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore draft text for this editor session.
|
||||
*
|
||||
* Behavior:
|
||||
* - Applies text immediately.
|
||||
* - Marks current session as "draft restored", so async file loading won't overwrite it.
|
||||
* - Marks editor state as dirty (needs save) and refreshes menu state.
|
||||
*
|
||||
* zh-CN:
|
||||
* 为本次编辑会话恢复草稿文本.
|
||||
*
|
||||
* 行为:
|
||||
* - 立即应用文本.
|
||||
* - 标记本会话为 "已恢复草稿", 防止异步文件加载覆盖.
|
||||
* - 标记为未保存并刷新菜单状态.
|
||||
*/
|
||||
fun restoreDraftTextForThisSession(text: String) {
|
||||
mDraftRestoredInThisSession = true
|
||||
mRestoredText = text
|
||||
editor.text = text
|
||||
markRestoredDraftAsDirty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a restored draft as unsaved and refresh menu state.
|
||||
*
|
||||
* Rationale:
|
||||
* - Draft restore should not be treated as "saved baseline".
|
||||
* - Otherwise Save button may appear disabled and exit-confirm may not show.
|
||||
*
|
||||
* zh-CN:
|
||||
* 将已恢复的草稿标记为未保存, 并刷新菜单状态.
|
||||
*
|
||||
* 理由:
|
||||
* - 草稿恢复不应被当作 "已保存基线".
|
||||
* - 否则保存按钮可能呈灰色, 且退出确认可能不弹出.
|
||||
*/
|
||||
fun markRestoredDraftAsDirty() {
|
||||
saveStickyDirty = true
|
||||
mHadDirectEditSinceSave = true
|
||||
syncPrimaryMenuState()
|
||||
}
|
||||
|
||||
private fun handleText(intent: Intent): Observable<String> {
|
||||
val content = intent.getStringExtra(EXTRA_CONTENT)
|
||||
if (content != null) {
|
||||
@@ -1045,6 +1109,18 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
|
||||
mEditorLoading = true
|
||||
syncPrimaryMenuState()
|
||||
|
||||
// If draft has been restored in this session, do NOT overwrite it with async file load results.
|
||||
// Still end loading flags and sync menu state to keep UI consistent.
|
||||
//
|
||||
// zh-CN:
|
||||
// 若本会话已恢复草稿, 则不要用异步文件加载结果覆盖它.
|
||||
// 但仍需结束 loading 标记并同步菜单状态, 保持 UI 一致.
|
||||
if (mDraftRestoredInThisSession) {
|
||||
mEditorLoading = false
|
||||
syncPrimaryMenuState()
|
||||
return
|
||||
}
|
||||
|
||||
if (mRestoredText != null) {
|
||||
editor.text = mRestoredText!!
|
||||
mRestoredText = null
|
||||
@@ -1104,7 +1180,7 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncPrimaryMenuState() {
|
||||
internal fun syncPrimaryMenuState() {
|
||||
// Unified primary buttons state.
|
||||
// zh-CN: 统一主按钮状态.
|
||||
when {
|
||||
|
||||
Reference in New Issue
Block a user