mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Always show the File Tree button in the toolbar (#1019)
* Implement a preference to always show the File Tree action in the toolbar * Create the ExtendedMenuToolbar which tries to keep as many actions as possible outside of the overflow menu * Repurpose the show-file-tree-button preference into hide-file-tree-button. Reason: the button is always technically shown, especially if we consider users with large screens. * Move ExtendedMenuToolbar.kt into com.itsaky.androidide.ui in :common. Fix resource compilation error by escaping the apostrophe. * Check whether the gesture navigation is enabled for setting the default value of idepref_editor_hide_file_tree_button * Remove an unnecessary onAttachedToWindow() override
This commit is contained in:
@@ -119,6 +119,7 @@ class DefaultActionsRegistry : ActionsRegistry() {
|
||||
|
||||
private fun addActionToMenu(menu: Menu, action: ActionItem, data: ActionData) {
|
||||
val context = data[Context::class.java]
|
||||
|
||||
val item: MenuItem = if (action is ActionMenu) {
|
||||
val sub = menu.addSubMenu(action.label)
|
||||
|
||||
@@ -141,6 +142,7 @@ class DefaultActionsRegistry : ActionsRegistry() {
|
||||
}
|
||||
|
||||
item.isEnabled = action.enabled
|
||||
|
||||
item.icon = action.icon?.apply {
|
||||
colorFilter = PorterDuffColorFilter(context!!.resolveAttr(R.attr.colorOnSurface), SRC_ATOP)
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
package com.itsaky.androidide.actions.etc
|
||||
|
||||
import android.content.Context
|
||||
import android.view.MenuItem
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import com.itsaky.androidide.resources.R
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
import com.itsaky.androidide.actions.EditorActivityAction
|
||||
import com.itsaky.androidide.preferences.internal.hideFileTreeButton
|
||||
|
||||
/** @author Akash Yadav */
|
||||
class FileTreeAction(context: Context) : EditorActivityAction() {
|
||||
@@ -51,4 +53,12 @@ class FileTreeAction(context: Context) : EditorActivityAction() {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getShowAsActionFlags(data: ActionData): Int {
|
||||
return if (hideFileTreeButton) {
|
||||
MenuItem.SHOW_AS_ACTION_NEVER
|
||||
} else {
|
||||
MenuItem.SHOW_AS_ACTION_IF_ROOM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ import com.itsaky.androidide.utils.DialogUtils.newYesNoDialog
|
||||
import com.itsaky.androidide.utils.IntentUtils.openImage
|
||||
import com.itsaky.androidide.utils.UniqueNameBuilder
|
||||
import com.itsaky.androidide.utils.flashSuccess
|
||||
import io.github.rosemoe.sora.event.ContentChangeEvent
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import java.io.File
|
||||
@@ -439,15 +438,20 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
|
||||
open fun ensureToolbarMenu(menu: Menu) {
|
||||
menu.clear()
|
||||
|
||||
val data = ActionData()
|
||||
val currentEditor = getCurrentEditor()
|
||||
|
||||
data.put(Context::class.java, this)
|
||||
data.put(CodeEditorView::class.java, currentEditor)
|
||||
|
||||
if (currentEditor != null) {
|
||||
data.put(IDEEditor::class.java, currentEditor.editor)
|
||||
data.put(File::class.java, currentEditor.editor.file)
|
||||
}
|
||||
|
||||
getInstance().fillMenu(data, EDITOR_TOOLBAR, menu)
|
||||
binding.editorToolbar.updateMenuDisplay()
|
||||
}
|
||||
|
||||
private fun closeAll(runAfter: () -> Unit = {}) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.itsaky.androidide.preferences.internal.FLAG_PASSWORD
|
||||
import com.itsaky.androidide.preferences.internal.FONT_LIGATURES
|
||||
import com.itsaky.androidide.preferences.internal.FONT_SIZE
|
||||
import com.itsaky.androidide.preferences.internal.PRINTABLE_CHARS
|
||||
import com.itsaky.androidide.preferences.internal.HIDE_FILE_TREE_BUTTON
|
||||
import com.itsaky.androidide.preferences.internal.TAB_SIZE
|
||||
import com.itsaky.androidide.preferences.internal.USE_CUSTOM_FONT
|
||||
import com.itsaky.androidide.preferences.internal.USE_ICU
|
||||
@@ -51,6 +52,7 @@ import com.itsaky.androidide.preferences.internal.drawLineBreak
|
||||
import com.itsaky.androidide.preferences.internal.drawTrailingWs
|
||||
import com.itsaky.androidide.preferences.internal.fontLigatures
|
||||
import com.itsaky.androidide.preferences.internal.fontSize
|
||||
import com.itsaky.androidide.preferences.internal.hideFileTreeButton
|
||||
import com.itsaky.androidide.preferences.internal.tabSize
|
||||
import com.itsaky.androidide.preferences.internal.useCustomFont
|
||||
import com.itsaky.androidide.preferences.internal.useIcu
|
||||
@@ -92,6 +94,7 @@ private class CommonConfigurations(
|
||||
addPreference(UseCustomFont())
|
||||
addPreference(UseSoftTab())
|
||||
addPreference(WordWrap())
|
||||
addPreference(HideFileTreeButton())
|
||||
addPreference(UseMagnifier())
|
||||
addPreference(UseICU())
|
||||
addPreference(AutoSave())
|
||||
@@ -257,6 +260,14 @@ private class WordWrap(
|
||||
override val icon: Int? = drawable.ic_wrap_text,
|
||||
) : SwitchPreference(setValue = ::wordwrap::set, getValue = ::wordwrap::get)
|
||||
|
||||
@Parcelize
|
||||
private class HideFileTreeButton(
|
||||
override val key: String = HIDE_FILE_TREE_BUTTON,
|
||||
override val title: Int = string.idepref_editor_hide_file_tree_button_title,
|
||||
override val summary: Int? = string.idepref_editor_hide_file_tree_button_summary,
|
||||
override val icon: Int? = drawable.ic_folder,
|
||||
) : SwitchPreference(setValue = ::hideFileTreeButton::set, getValue = ::hideFileTreeButton::get)
|
||||
|
||||
@Parcelize
|
||||
private class UseMagnifier(
|
||||
override val key: String = USE_MAGNIFER,
|
||||
@@ -279,11 +290,10 @@ private class CompletionsMatchLower(
|
||||
override val title: Int = string.idepref_java_matchLower_title,
|
||||
override val summary: Int? = string.idepref_java_matchLower_summary,
|
||||
override val icon: Int? = drawable.ic_text_lower,
|
||||
) :
|
||||
SwitchPreference(
|
||||
) : SwitchPreference(
|
||||
setValue = ::completionsMatchLower::set,
|
||||
getValue = ::completionsMatchLower::get
|
||||
)
|
||||
)
|
||||
|
||||
@Parcelize
|
||||
private class VisibiblePasswordFlag(
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
android:background="?attr/colorSurface"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
<com.itsaky.androidide.ui.ExtendedMenuToolbar
|
||||
android:id="@+id/editor_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.AttributeSet
|
||||
import android.view.MenuItem
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.appcompat.view.menu.MenuItemImpl
|
||||
import androidx.appcompat.widget.ActionMenuView
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.iterator
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import kotlin.math.floor
|
||||
|
||||
/** @author Smooth E */
|
||||
class ExtendedMenuToolbar : MaterialToolbar {
|
||||
|
||||
private var cachedRequiredSpace: Float = 0f
|
||||
private var cachedItemWidth: Int = 0
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
viewTreeObserver.addOnGlobalLayoutListener(
|
||||
object: ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
updateMenuDisplay()
|
||||
viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun updateMenuDisplay() {
|
||||
if (cachedRequiredSpace == 0f || cachedItemWidth == 0) {
|
||||
var requiredSpace = 0f
|
||||
var actionMenuView: ActionMenuView? = null
|
||||
|
||||
for (child in children) {
|
||||
if (child is ActionMenuView) {
|
||||
actionMenuView = child
|
||||
continue
|
||||
}
|
||||
|
||||
requiredSpace = maxOf(child.x + child.width - x, requiredSpace)
|
||||
}
|
||||
|
||||
cachedRequiredSpace = requiredSpace
|
||||
|
||||
if (actionMenuView?.getChildAt(0) == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val itemWidth = actionMenuView.getChildAt(0).width
|
||||
|
||||
if (itemWidth == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
cachedItemWidth = itemWidth
|
||||
}
|
||||
|
||||
val maxItemCount = floor((width - cachedRequiredSpace) / cachedItemWidth).toInt()
|
||||
|
||||
var itemsModified = 0
|
||||
for (item in menu.iterator()) {
|
||||
val flag = getShowAsActionFlag(item)
|
||||
|
||||
val ignoreItem =
|
||||
flag == MenuItemImpl.SHOW_AS_ACTION_NEVER ||
|
||||
flag == MenuItemImpl.SHOW_AS_ACTION_WITH_TEXT
|
||||
|
||||
if (ignoreItem) {
|
||||
continue
|
||||
}
|
||||
|
||||
itemsModified++
|
||||
|
||||
if (itemsModified < maxItemCount) {
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
||||
} else {
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/48392871
|
||||
@SuppressLint("RestrictedApi")
|
||||
private fun getShowAsActionFlag(item: MenuItem): Int {
|
||||
val itemImpl = item as MenuItemImpl
|
||||
return if (itemImpl.requiresActionButton()) MenuItemImpl.SHOW_AS_ACTION_ALWAYS
|
||||
else if (itemImpl.requestsActionButton()) MenuItemImpl.SHOW_AS_ACTION_IF_ROOM
|
||||
else if (itemImpl.showsTextAsAction()) MenuItemImpl.SHOW_AS_ACTION_WITH_TEXT
|
||||
else MenuItemImpl.SHOW_AS_ACTION_NEVER
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.annotation.IntDef
|
||||
|
||||
// Source: https://gist.github.com/Thorsten1976/07d61b3f697364e5f1c08ae076641d58
|
||||
|
||||
object NavigationBar {
|
||||
/** Classic three-button navigation (Back, Home, Recent Apps) */
|
||||
const val MODE_THREE_BUTTONS = 0
|
||||
|
||||
/** Two-button navigation (Android P navigation mode: Back, combined Home and Recent Apps) */
|
||||
const val MODE_TWO_BUTTONS = 1
|
||||
|
||||
/** Full screen gesture mode (introduced with Android Q) */
|
||||
const val MODE_GESTURES = 2
|
||||
|
||||
/**
|
||||
* Returns the interaction mode of the system navigation bar as defined by
|
||||
* [NavigationBarInteractionMode]. Depending on the Android version and OEM implementation,
|
||||
* users might change the interaction mode via system settings: System>Gestures>System Navigation.
|
||||
* This can lead to conflicts with apps that use specific system-gestures internally for
|
||||
* navigation (i. e. swiping), especially if the Android 10 full screen gesture mode is enabled.
|
||||
*
|
||||
*
|
||||
* Before Android P the system has used a classic three button navigation. Starting with Android P
|
||||
* a two-button-based interaction mode was introduced (also referred as Android P navigation).
|
||||
*
|
||||
*
|
||||
* Android Q changed the interaction and navigation concept to a gesture approach, the so called
|
||||
* full screen gesture mode: system-wide gestures are used to navigate within an app or to
|
||||
* interact with the system (i. e. back-navigation, open the home-screen, changing apps, or toggle
|
||||
* a fullscreen mode).
|
||||
*
|
||||
*
|
||||
* Based on [https://stackoverflow.com/questions/56689210/how-to-detect-full-screen-gesture-mode-in-android-10](https://stackoverflow.com/questions/56689210/how-to-detect-full-screen-gesture-mode-in-android-10)
|
||||
*
|
||||
* @param context The [Context] that is used to read the resource configuration.
|
||||
* @return the [NavigationBarInteractionMode]
|
||||
* @see .MODE_THREE_BUTTONS
|
||||
*
|
||||
* @see .MODE_TWO_BUTTONS
|
||||
*
|
||||
* @see .MODE_GESTURES
|
||||
*/
|
||||
@SuppressLint("DiscouragedApi")
|
||||
@NavigationBarInteractionMode
|
||||
fun getInteractionMode(context: Context): Int {
|
||||
val resources = context.resources
|
||||
|
||||
val resourceId = resources.getIdentifier(
|
||||
"config_navBarInteractionMode",
|
||||
"integer",
|
||||
"android"
|
||||
)
|
||||
|
||||
return if (resourceId > 0)
|
||||
resources.getInteger(resourceId)
|
||||
else
|
||||
MODE_THREE_BUTTONS
|
||||
}
|
||||
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@IntDef(MODE_THREE_BUTTONS, MODE_TWO_BUTTONS, MODE_GESTURES)
|
||||
internal annotation class NavigationBarInteractionMode
|
||||
|
||||
}
|
||||
+17
@@ -17,6 +17,9 @@
|
||||
|
||||
package com.itsaky.androidide.preferences.internal
|
||||
|
||||
import com.itsaky.androidide.app.BaseApplication
|
||||
import com.itsaky.androidide.utils.NavigationBar
|
||||
|
||||
const val COMPLETIONS_MATCH_LOWER = "idepref_editor_completions_matchLower"
|
||||
|
||||
const val FLAG_WS_LEADING = "idepref_editor_wsLeading"
|
||||
@@ -31,6 +34,7 @@ const val AUTO_SAVE = "idepref_editor_autoSave"
|
||||
const val FONT_LIGATURES = "idepref_editor_fontLigatures"
|
||||
const val FLAG_PASSWORD = "idepref_editor_flagPassword"
|
||||
const val WORD_WRAP = "idepref_editor_word_wrap"
|
||||
const val HIDE_FILE_TREE_BUTTON = "idepref_hide_file_tree_button"
|
||||
const val USE_MAGNIFER = "idepref_editor_use_magnifier"
|
||||
const val USE_ICU = "idepref_editor_useIcu"
|
||||
const val USE_SOFT_TAB = "idepref_editor_useSoftTab"
|
||||
@@ -113,6 +117,19 @@ var wordwrap: Boolean
|
||||
prefManager.putBoolean(WORD_WRAP, value)
|
||||
}
|
||||
|
||||
/** By default the File Tree button is hidden into the overflow menu on devices where
|
||||
* Gesture Navigation is enabled at the moment of accessing the preference.
|
||||
*/
|
||||
var hideFileTreeButton: Boolean
|
||||
get() {
|
||||
val navigationMode = NavigationBar.getInteractionMode(BaseApplication.getBaseInstance())
|
||||
val gestureNavigationDisabled = navigationMode != NavigationBar.MODE_GESTURES
|
||||
return prefManager.getBoolean(HIDE_FILE_TREE_BUTTON, gestureNavigationDisabled)
|
||||
}
|
||||
set(value) {
|
||||
prefManager.putBoolean(HIDE_FILE_TREE_BUTTON, value)
|
||||
}
|
||||
|
||||
var useMagnifier: Boolean
|
||||
get() = prefManager.getBoolean(USE_MAGNIFER, true)
|
||||
set(value) {
|
||||
|
||||
@@ -22,4 +22,4 @@ import com.itsaky.androidide.managers.PreferenceManager
|
||||
|
||||
/** @author Akash Yadav */
|
||||
val prefManager: PreferenceManager
|
||||
get() = BaseApplication.getBaseInstance().prefManager
|
||||
get() = BaseApplication.getBaseInstance().prefManager
|
||||
|
||||
@@ -208,6 +208,8 @@
|
||||
<string name="idepref_editor_drawhexcolors_summary">Should the editor draw hex color string?</string>
|
||||
<string name="idepref_editor_word_wrap_title">Word Wrap</string>
|
||||
<string name="idepref_editor_word_wrap_summary">Use wordwrap of the editor</string>
|
||||
<string name="idepref_editor_hide_file_tree_button_title">Hide the File Tree button</string>
|
||||
<string name="idepref_editor_hide_file_tree_button_summary">Move the "File Tree" button into the overflow menu of the editor\'s toolbar.</string>
|
||||
<string name="idepref_editor_use_magnifier_title">Enable Magnifier</string>
|
||||
<string name="idepref_editor_use_magnifier_summary">Magnify text at cursor position while selecting text in editor</string>
|
||||
<string name="idepref_editor_title">Editor</string>
|
||||
|
||||
Reference in New Issue
Block a user