Replaced ActionMode searcher with bottom bar

This commit is contained in:
Akash Yadav
2022-10-08 22:04:39 +05:30
parent 294deb088e
commit 3f02c7fed8
15 changed files with 385 additions and 394 deletions
@@ -105,10 +105,7 @@ interface ActionItem {
* Location marker for action items shown in 'Code actions' submenu in editor's text action
* menu.
*/
EDITOR_CODE_ACTIONS("editor.codeActions"),
/** Location marker for action items show in 'Find & Replace' action mode menu in editor. */
EDITOR_SEARCH_ACTION_MODE("editor.searchActionMode");
EDITOR_CODE_ACTIONS("editor.codeActions");
override fun toString(): String {
return id
@@ -1,45 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.actions.editor.find
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
/**
* 'Go to next' action in editor's search action mode.
* @author Akash Yadav
*/
class GoToNextAction() : SearchActionModeAction() {
override val id: String = "editor.find.next"
constructor(context: Context) : this() {
label = context.getString(R.string.next)
icon = ContextCompat.getDrawable(context, R.drawable.ic_search_next)
}
override fun execAction(data: ActionData): Any {
val editor = getEditor(data)!!
if (!editor.searcher.hasQuery()) {
return false
}
return editor.searcher.gotoNext()
}
}
@@ -1,46 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.actions.editor.find
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.R
import com.itsaky.androidide.R.string
import com.itsaky.androidide.actions.ActionData
/**
* 'Go to previous' action in editor's search action mode.
* @author Akash Yadav
*/
class GoToPreviousAction() : SearchActionModeAction() {
override val id: String = "editor.find.previous"
constructor(context: Context) : this() {
label = context.getString(string.previous)
icon = ContextCompat.getDrawable(context, R.drawable.ic_search_previous)
}
override fun execAction(data: ActionData): Any {
val editor = getEditor(data)!!
if (!editor.searcher.hasQuery()) {
return false
}
return editor.searcher.gotoPrevious()
}
}
@@ -1,33 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.actions.editor.find
import android.view.MenuItem
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.ActionItem.Location
import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_SEARCH_ACTION_MODE
import com.itsaky.androidide.actions.EditorRelatedAction
/** @author Akash Yadav */
abstract class SearchActionModeAction : EditorRelatedAction() {
override var location: Location = EDITOR_SEARCH_ACTION_MODE
override fun getShowAsActionFlags(data: ActionData): Int {
return MenuItem.SHOW_AS_ACTION_IF_ROOM
}
}
@@ -1,82 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.actions.editor.find
import android.app.SearchManager
import android.content.Context
import android.text.TextUtils
import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.app.IDEActivity
import com.itsaky.androidide.views.editor.IDEEditor
import io.github.rosemoe.sora.widget.EditorSearcher.SearchOptions
/**
* The [SearchView] action in the search action mode.
*
* @author Akash Yadav
*/
class SearchViewActionItem() : SearchActionModeAction() {
override val id: String = "editor.search.view"
constructor(context: Context) : this() {
label = context.getString(R.string.text_to_search)
icon = ContextCompat.getDrawable(context, R.drawable.ic_search)
}
override fun execAction(data: ActionData): Any {
return Unit
}
override fun createActionView(data: ActionData): View? {
val editor = getEditor(data) ?: return null
val searchManager = editor.context.getSystemService(Context.SEARCH_SERVICE) as SearchManager
val search = SearchView(editor.context)
search.isIconified = false
search.queryHint = editor.context.getString(R.string.text_to_search)
search.setSearchableInfo(
searchManager.getSearchableInfo((editor.context as IDEActivity).componentName)
)
search.setOnQueryTextListener(OnQueryListener(editor))
search.performClick()
search.setIconifiedByDefault(false)
return search
}
private class OnQueryListener(val editor: IDEEditor) : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
editor.searcher.gotoNext()
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
if (TextUtils.isEmpty(newText)) {
editor.searcher.stopSearch()
return false
}
editor.searcher.search(newText!!, SearchOptions(false, false))
return false
}
}
}
@@ -28,10 +28,6 @@ import com.itsaky.androidide.actions.editor.CutAction
import com.itsaky.androidide.actions.editor.ExpandSelectionAction
import com.itsaky.androidide.actions.editor.PasteAction
import com.itsaky.androidide.actions.editor.SelectAllAction
import com.itsaky.androidide.actions.editor.find.GoToNextAction
import com.itsaky.androidide.actions.editor.find.GoToPreviousAction
import com.itsaky.androidide.actions.editor.find.ReplaceAction
import com.itsaky.androidide.actions.editor.find.SearchViewActionItem
import com.itsaky.androidide.actions.etc.DaemonStatusAction
import com.itsaky.androidide.actions.etc.FileTreeAction
import com.itsaky.androidide.actions.etc.FindActionMenu
@@ -72,12 +68,6 @@ class EditorActivityActions {
registry.registerAction(CopyAction(context))
registry.registerAction(PasteAction(context))
registry.registerAction(FormatCodeAction(context))
// editor search actions
registry.registerAction(SearchViewActionItem(context))
registry.registerAction(GoToNextAction(context))
registry.registerAction(GoToPreviousAction(context))
registry.registerAction(ReplaceAction(context))
}
@JvmStatic
@@ -41,8 +41,11 @@ import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -68,6 +71,7 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.ILogger;
import com.itsaky.androidide.utils.LSPUtils;
import com.itsaky.androidide.utils.SingleTextWatcher;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.inflater.values.ValuesTableFactory;
@@ -83,6 +87,7 @@ import java.util.concurrent.CompletableFuture;
import io.github.rosemoe.sora.lang.EmptyLanguage;
import io.github.rosemoe.sora.lang.Language;
import io.github.rosemoe.sora.text.LineSeparator;
import io.github.rosemoe.sora.widget.EditorSearcher;
import io.github.rosemoe.sora.widget.component.Magnifier;
/**
@@ -91,18 +96,20 @@ import io.github.rosemoe.sora.widget.component.Magnifier;
* @author Akash Yadav
*/
@SuppressLint("ViewConstructor") // This view is always dynamically created.
public class CodeEditorView extends FrameLayout {
public class CodeEditorView extends LinearLayout {
private static final ILogger LOG = ILogger.newInstance("CodeEditorView");
private final File file;
private final LayoutCodeEditorBinding binding;
private final EditorSearchLayout searchLayout;
public CodeEditorView(
@NonNull Context context, @NonNull File file, final @NonNull Range selection) {
super(context);
this.file = file;
this.binding = LayoutCodeEditorBinding.inflate(LayoutInflater.from(context));
final var inflater = LayoutInflater.from(context);
this.binding = LayoutCodeEditorBinding.inflate(inflater);
this.binding.editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
this.binding.editor.setHighlightCurrentBlock(true);
this.binding.editor.getProps().autoCompletionOnComposing = true;
@@ -110,10 +117,16 @@ public class CodeEditorView extends FrameLayout {
this.binding.editor.setColorScheme(new SchemeAndroidIDE());
this.binding.editor.setLineSeparator(LineSeparator.LF);
this.searchLayout = new EditorSearchLayout(context, this.binding.editor);
this.binding.diagnosticTextContainer.setVisibility(GONE);
removeAllViews();
addView(this.binding.getRoot());
setOrientation(VERTICAL);
addView(this.binding.getRoot(), new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f));
addView(
this.searchLayout, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
selection.validate();
CompletableFuture.runAsync(
@@ -399,7 +412,12 @@ public class CodeEditorView extends FrameLayout {
}
public void beginSearch() {
binding.editor.beginSearchMode();
if (this.binding == null || searchLayout == null) {
LOG.warn("Editor layout is null content=" + binding + ", searchLayout=" + searchLayout);
return;
}
searchLayout.beginSearchMode();
}
/** Mark this files as saved. Even if it not saved. */
@@ -0,0 +1,169 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.views.editor
import android.annotation.SuppressLint
import android.content.Context
import android.text.TextUtils
import android.text.TextWatcher
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.FrameLayout
import android.widget.PopupMenu
import com.itsaky.androidide.R
import com.itsaky.androidide.databinding.LayoutFindInFileBinding
import com.itsaky.androidide.utils.SingleTextWatcher
import com.itsaky.androidide.views.editor.ReplaceAction.doReplace
import io.github.rosemoe.sora.widget.EditorSearcher.SearchOptions
import java.util.regex.Pattern
/**
* The search layout in [IDEEditor].
*
* @author Akash Yadav
*/
@SuppressLint("ViewConstructor") // Always created dynamically
class EditorSearchLayout(context: Context, val editor: IDEEditor) : FrameLayout(context) {
private var searchInputTextWatcher: TextWatcher? = null
private var searchOptions = SearchOptions(true, false)
private val findInFileBinding: LayoutFindInFileBinding
private val optionsMenu: PopupMenu
init {
findInFileBinding = LayoutFindInFileBinding.inflate(LayoutInflater.from(context))
findInFileBinding.prev.setOnClickListener(::onSearchActionClick)
findInFileBinding.next.setOnClickListener(::onSearchActionClick)
findInFileBinding.replace.setOnClickListener(::onSearchActionClick)
findInFileBinding.close.setOnClickListener(::onSearchActionClick)
optionsMenu = PopupMenu(context, findInFileBinding.moreOptions, Gravity.TOP)
optionsMenu.menu.add(0, 0, 0, R.string.msg_ignore_case).apply {
isCheckable = true
isChecked = true
}
optionsMenu.menu.add(0, 1, 0, R.string.msg_use_regex).apply {
isCheckable = true
isChecked = false
}
optionsMenu.setOnMenuItemClickListener {
return@setOnMenuItemClickListener if (it.isCheckable) {
it.isChecked = !it.isChecked
val ignoreCase = searchOptions.ignoreCase
val regex = searchOptions.useRegex
searchOptions =
when (it.itemId) {
0 -> SearchOptions(it.isChecked, regex)
1 -> SearchOptions(ignoreCase, it.isChecked)
else -> searchOptions
}
editor.searcher?.updateSearchOptions(searchOptions)
true
} else false
}
findInFileBinding.root.visibility = GONE
findInFileBinding.moreOptions.setOnClickListener { optionsMenu.show() }
addView(
findInFileBinding.root,
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
)
}
fun beginSearchMode() {
searchInputTextWatcher = SearchInputTextChangeListener(editor)
findInFileBinding.searchInput.addTextChangedListener(searchInputTextWatcher)
findInFileBinding.searchInput.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_NEXT) {
onSearchActionClick(findInFileBinding.next)
}
false
}
findInFileBinding.root.visibility = VISIBLE
}
private fun onSearchActionClick(v: View) {
val searcher = editor.searcher
if (v.id == findInFileBinding.close.id) {
if (this.searchInputTextWatcher == null) {
return
}
findInFileBinding.searchInput.removeTextChangedListener(this.searchInputTextWatcher)
findInFileBinding.root.visibility = GONE
this.searchInputTextWatcher = null
}
if (!searcher.hasQuery()) {
return
}
if (v.id == findInFileBinding.prev.id) {
searcher.gotoPrevious()
return
}
if (v.id == findInFileBinding.next.id) {
searcher.gotoNext()
return
}
if (v.id == findInFileBinding.replace.id) {
doReplace(editor)
}
}
inner class SearchInputTextChangeListener(val editor: IDEEditor?) : SingleTextWatcher() {
override fun onTextChanged(
s: CharSequence,
start: Int,
before: Int,
count: Int,
) {
if (editor == null) {
return
}
if (TextUtils.isEmpty(s)) {
editor.searcher.stopSearch()
return
}
// Handle bad regexp
val query =
s.toString().let {
if (searchOptions.useRegex) {
try {
Pattern.compile(it)
it
} catch (error: Throwable) {
""
}
} else {
it
}
}
if (query.isNotBlank()) {
editor.searcher.search(query, searchOptions)
}
}
}
}
@@ -870,12 +870,7 @@ public class IDEEditor extends CodeEditor implements com.itsaky.androidide.edito
@Override
public void beginSearchMode() {
final var callback = new SearchActionMode(this);
if (getContext() instanceof AppCompatActivity) {
((AppCompatActivity)getContext()).startSupportActionMode(callback);
} else {
LOG.error("Unable start search action mode. Activity must inherit AppCompatActivity.");
}
throw new UnsupportedOperationException("Search ActionMode is not supported. Use CodeEditorView.beginSearch() instead.");
}
protected void dispatchDocumentSaveEvent() {
@@ -1,87 +1,69 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.actions.editor.find
import android.content.Context
import android.view.LayoutInflater
import androidx.appcompat.view.ActionMode
import androidx.core.content.ContextCompat
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.databinding.LayoutEditorFindReplaceBinding
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.ILogger
/**
* Shows dialog to replace one or all the matched text in editor searcher.
* @author Akash Yadav
*/
class ReplaceAction() : SearchActionModeAction() {
override val id: String = "editor.search.replace"
private val log = ILogger.newInstance(javaClass.simpleName)
constructor(context: Context) : this() {
label = context.getString(R.string.replace)
icon = ContextCompat.getDrawable(context, R.drawable.ic_search_replace)
}
override fun execAction(data: ActionData): Any {
val context = data[Context::class.java]!!
val editor = getEditor(data)!!
if (!editor.searcher.hasQuery()) {
return false
}
val binding = LayoutEditorFindReplaceBinding.inflate(LayoutInflater.from(context))
val builder = DialogUtils.newMaterialDialogBuilder(context)
builder.setTitle(R.string.replace)
builder.setView(binding.root)
builder.setNegativeButton(android.R.string.cancel, null)
builder.setPositiveButton(R.string.replace) { dialog, _ ->
dialog.dismiss()
val input = binding.replacementInput.editText
if (input == null) {
log.error("Unable to perform replace action. Input field is null")
return@setPositiveButton
}
editor.searcher.replaceThis(input.text.toString())
}
builder.setNeutralButton(R.string.replaceAll) { dialog, _ ->
dialog.dismiss()
val input = binding.replacementInput.editText
if (input == null) {
log.error("Unable to perform replace action. Input field is null")
return@setNeutralButton
}
editor.searcher.replaceAllAsync(input.text.toString()).whenComplete { _, error ->
if (error != null) {
log.error("Unable to replace all matched text", error)
} else {
editor.post { data[ActionMode::class.java]?.finish() }
}
}
}
builder.show()
return true
}
}
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.views.editor
import android.view.LayoutInflater
import com.itsaky.androidide.R
import com.itsaky.androidide.databinding.LayoutEditorFindReplaceBinding
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.ILogger
/**
* Handles the replace action while searching in file.
*
* @author Akash Yadav
*/
object ReplaceAction {
private val log = ILogger.newInstance("ReplaceAction")
@JvmStatic
fun doReplace(editor: IDEEditor) {
val context = editor.context
val binding = LayoutEditorFindReplaceBinding.inflate(LayoutInflater.from(context))
val builder = DialogUtils.newMaterialDialogBuilder(context)
builder.setTitle(R.string.replace)
builder.setView(binding.root)
builder.setNegativeButton(android.R.string.cancel, null)
builder.setPositiveButton(R.string.replace) { dialog, _ ->
dialog.dismiss()
val input = binding.replacementInput.editText
if (input == null) {
log.error("Unable to perform replace action. Input field is null")
return@setPositiveButton
}
editor.searcher.replaceThis(input.text.toString())
}
builder.setNeutralButton(R.string.replaceAll) { dialog, _ ->
dialog.dismiss()
val input = binding.replacementInput.editText
if (input == null) {
log.error("Unable to perform replace action. Input field is null")
return@setNeutralButton
}
editor.searcher.replaceAllAsync(input.text.toString()).whenComplete { _, error ->
if (error != null) {
log.error("Unable to replace all matched text", error)
}
}
}
builder.show()
}
}
@@ -1,76 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.views.editor
import android.annotation.SuppressLint
import android.content.Context
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.view.ActionMode
import androidx.appcompat.view.menu.MenuBuilder
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.ActionItem
import com.itsaky.androidide.actions.ActionsRegistry
import io.github.rosemoe.sora.widget.CodeEditor
/**
* Action mode for find and replace functions in editor.
* @author Akash Yadav
*/
class SearchActionMode(val editor: IDEEditor) : ActionMode.Callback {
@SuppressLint("RestrictedApi")
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
if (mode == null) {
return false
}
if (menu is MenuBuilder) {
menu.setOptionalIconsVisible(true)
}
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
if (mode == null || menu == null) {
return false
}
menu.clear()
val registry = ActionsRegistry.getInstance()
registry.fillMenu(createData(mode), ActionItem.Location.EDITOR_SEARCH_ACTION_MODE, menu)
return true
}
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
return true
}
override fun onDestroyActionMode(mode: ActionMode?) {
editor.searcher.stopSearch()
}
private fun createData(mode: ActionMode): ActionData {
return ActionData().apply {
put(Context::class.java, editor.context)
put(IDEEditor::class.java, editor)
put(CodeEditor::class.java, editor)
put(ActionMode::class.java, mode)
}
}
}
@@ -50,6 +50,10 @@ open class IDEEditorSearcher(editor: IDEEditor) : EditorSearcher(editor) {
throw RuntimeException("Unable get instance of editor", error)
}
}
fun updateSearchOptions(searchOptions: SearchOptions) {
this.searchOptions = searchOptions
}
fun replaceAllAsync(replacement: String): CompletableFuture<Void> {
markSearching()
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primaryDarkColor">
<EditText
android:id="@+id/search_input"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:hint="@string/text_to_search"
android:imeOptions="actionNext"
android:importantForAutofill="no"
android:inputType="text"
android:maxLines="1"
app:layout_constraintEnd_toStartOf="@id/more_options"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/more_options"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:background="@android:color/transparent"
android:src="@drawable/ic_more_vert_black_20dp"
android:tint="@color/primaryIconColor"
app:layout_constraintBottom_toBottomOf="@id/search_input"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/search_input"
app:layout_constraintTop_toTopOf="@id/search_input" />
<com.google.android.material.button.MaterialButton
android:id="@+id/prev"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="0dp"
android:layout_height="40dp"
android:text="@string/previous"
android:textAllCaps="true"
android:textColor="@color/primaryTextColor"
app:layout_constraintEnd_toStartOf="@+id/next"
app:layout_constraintHorizontal_weight="0.25"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_input"
app:rippleColor="@color/secondaryColor" />
<com.google.android.material.button.MaterialButton
android:id="@+id/next"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="0dp"
android:layout_height="40dp"
android:text="@string/next"
android:textAllCaps="true"
android:textColor="@color/primaryTextColor"
app:layout_constraintEnd_toStartOf="@+id/replace"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_weight="0.25"
app:layout_constraintStart_toEndOf="@+id/prev"
app:layout_constraintTop_toBottomOf="@+id/search_input"
app:rippleColor="@color/secondaryColor" />
<com.google.android.material.button.MaterialButton
android:id="@+id/replace"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="0dp"
android:layout_height="40dp"
android:text="@string/replace"
android:textAllCaps="true"
android:textColor="@color/primaryTextColor"
app:layout_constraintEnd_toStartOf="@+id/close"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_weight="0.25"
app:layout_constraintStart_toEndOf="@+id/next"
app:layout_constraintTop_toBottomOf="@+id/search_input"
app:rippleColor="@color/secondaryColor" />
<com.google.android.material.button.MaterialButton
android:id="@+id/close"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="0dp"
android:layout_height="40dp"
android:text="@string/btn_close"
android:textAllCaps="true"
android:textColor="@color/primaryTextColor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_weight="0.25"
app:layout_constraintStart_toEndOf="@+id/replace"
app:layout_constraintTop_toBottomOf="@+id/search_input"
app:rippleColor="@color/secondaryColor" />
</androidx.constraintlayout.widget.ConstraintLayout>
+3
View File
@@ -361,4 +361,7 @@
<string name="idepref_editor_useSoftTabs_summary">Choose whether to use spaces instead of tab character (\\t).</string>
<string name="btn_sponsor">Sponsor</string>
<string name="btn_docs">Documentation</string>
<string name="btn_close">Close</string>
<string name="msg_ignore_case">Ignore case</string>
<string name="msg_use_regex">Use regex</string>
</resources>
Binary file not shown.