Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ interface ActionItem {
val itemId: Int
get() = id.hashCode()

/**
* Whether the editor toolbar should fully remove this action when [visible] is false,
* instead of the legacy behaviour of keeping it and only greying out when disabled.
* Built-in actions keep the legacy behaviour (default false); plugin-contributed
* toolbar actions opt in by overriding this to true.
*/
val honorVisibility: Boolean
get() = false

/**
* Prepare the action. Subclasses can modify the visual properties of this action here.
*
Expand Down
1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,6 @@ dependencies {

implementation(projects.layouteditor)
implementation(projects.idetooltips)
implementation(projects.composePreview)
implementation(projects.gitCore)

// This is to build the tooling-api-impl project before the app is built
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.itsaky.androidide.actions

import android.content.Context
import android.util.Log
import android.view.MenuItem
import androidx.core.content.ContextCompat
import com.itsaky.androidide.R
import com.itsaky.androidide.plugins.extensions.ShowAsAction
import com.itsaky.androidide.plugins.extensions.ToolbarAction
import com.itsaky.androidide.plugins.manager.pluginCategory
import com.itsaky.androidide.plugins.manager.pluginTooltipTag
import com.itsaky.androidide.plugins.manager.ui.PluginDrawableResolver

/**
* Adapts a plugin-contributed [ToolbarAction] (from `UIExtension.getToolbarActions()`)
* into an editor-toolbar [ActionItem].
*
* Unlike [PluginActionItem] (which wraps `getMainMenuItems()`), this is the dedicated
* path for toolbar icons: it carries the action's own [ToolbarAction.order] so a plugin
* can position its icon among the built-in actions, and it opts into [honorVisibility]
* so the toolbar fully removes it (instead of greying it) when not applicable.
*/
class PluginToolbarActionItem(
context: Context,
private val toolbarAction: ToolbarAction,
val pluginId: String
) : EditorActivityAction() {

override val id: String = "plugin.toolbar.${toolbarAction.id}"

override val order: Int = toolbarAction.order

override val honorVisibility: Boolean get() = true

init {
label = toolbarAction.title
val iconResId = toolbarAction.icon
icon = if (iconResId != null) {
PluginDrawableResolver.resolve(iconResId, pluginId, context)
?: ContextCompat.getDrawable(context, R.drawable.ic_package)
} else {
ContextCompat.getDrawable(context, R.drawable.ic_package)
}
location = ActionItem.Location.EDITOR_TOOLBAR
requiresUIThread = true
}

override fun prepare(data: ActionData) {
super.prepare(data)
if (!visible) {
// EditorActivityAction.prepare() hides the action when the editor context is
// missing; respect that and skip the plugin providers.
return
}
runCatching {
enabled = toolbarAction.isEnabledProvider?.invoke() ?: toolbarAction.isEnabled
visible = toolbarAction.isVisibleProvider?.invoke() ?: toolbarAction.isVisible
}.onFailure { e ->
// A throwing/disposed plugin must never keep a stale icon on the toolbar.
Log.w("PluginToolbarActionItem", "prepare failed for '${toolbarAction.id}'", e)
enabled = false
visible = false
}
}

override fun getShowAsActionFlags(data: ActionData): Int = when (toolbarAction.showAsAction) {
ShowAsAction.ALWAYS -> MenuItem.SHOW_AS_ACTION_ALWAYS
ShowAsAction.IF_ROOM -> MenuItem.SHOW_AS_ACTION_IF_ROOM
ShowAsAction.NEVER -> MenuItem.SHOW_AS_ACTION_NEVER
ShowAsAction.WITH_TEXT -> MenuItem.SHOW_AS_ACTION_WITH_TEXT
ShowAsAction.COLLAPSE_ACTION_VIEW -> MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
}

override fun retrieveTooltipTag(isReadOnlyContext: Boolean): String =
pluginTooltipTag(pluginId, toolbarAction.id)

override fun retrieveTooltipCategory(): String = pluginCategory(pluginId)

override suspend fun execAction(data: ActionData): Any {
return try {
toolbarAction.action.invoke()
true
} catch (e: Exception) {
Log.e("PluginToolbarActionItem", "Error executing toolbar action '${toolbarAction.id}'", e)
false
}
}
}
Loading
Loading