Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 54
ADFA-2139: Add plugin sidebar slot limiting with manifest declaration and custom IdeNavigationRailView (max 12 items)#674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
e44eac3207ba11e819ec908b87ceFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.itsaky.androidide.actions | ||
| class SidebarSlotExceededException( | ||
| requestedSlots: Int, | ||
| availableSlots: Int, | ||
| pluginId: String? = null | ||
| ) : RuntimeException( | ||
| buildMessage(requestedSlots, availableSlots, pluginId) | ||
| ) { | ||
| companion object { | ||
| private fun buildMessage(requested: Int, available: Int, pluginId: String?): String { | ||
| val pluginInfo = pluginId?.let { " Plugin '$it'" } ?: "" | ||
| return "Sidebar slot limit exceeded.$pluginInfo declared $requested sidebar item(s), " + | ||
| "but only $available slot(s) available. " + | ||
| "IdeNavigationRailView supports a maximum of ${SidebarSlotManager.MAX_NAVIGATION_RAIL_ITEMS} items." | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package com.itsaky.androidide.actions | ||
| import java.util.concurrent.ConcurrentHashMap | ||
| import java.util.concurrent.atomic.AtomicInteger | ||
| object SidebarSlotManager { | ||
| const val MAX_NAVIGATION_RAIL_ITEMS = 12 | ||
| private val builtInItemCount = AtomicInteger(0) | ||
| private val reservedPluginSlots = ConcurrentHashMap<String, Int>() | ||
| fun setBuiltInItemCount(count: Int) { | ||
| require(count in 0..MAX_NAVIGATION_RAIL_ITEMS) { | ||
| "Built-in item count must be between 0 and $MAX_NAVIGATION_RAIL_ITEMS" | ||
| } | ||
| builtInItemCount.set(count) | ||
| } | ||
| fun getBuiltInItemCount(): Int = builtInItemCount.get() | ||
| fun getReservedPluginSlotCount(): Int = reservedPluginSlots.values.sum() | ||
| fun getTotalItemCount(): Int = builtInItemCount.get() + getReservedPluginSlotCount() | ||
| fun getAvailableSlotsForPlugins(): Int = | ||
| (MAX_NAVIGATION_RAIL_ITEMS - builtInItemCount.get() - getReservedPluginSlotCount()) | ||
| .coerceAtLeast(0) | ||
| fun canAddPluginItems(count: Int): Boolean = count <= getAvailableSlotsForPlugins() | ||
| fun getDeclaredSlots(pluginId: String): Int = reservedPluginSlots[pluginId] ?: 0 | ||
| @Throws(SidebarSlotExceededException::class) | ||
| fun reservePluginSlots(pluginId: String, count: Int) { | ||
| if (count <= 0) return | ||
| val available = getAvailableSlotsForPlugins() | ||
| if (count > available) { | ||
| throw SidebarSlotExceededException(count, available, pluginId) | ||
| } | ||
| reservedPluginSlots[pluginId] = count | ||
| } | ||
| fun releasePluginSlots(pluginId: String) { | ||
| reservedPluginSlots.remove(pluginId) | ||
| } | ||
| fun reset() { | ||
| builtInItemCount.set(0) | ||
| reservedPluginSlots.clear() | ||
| } | ||
Daniel-ADFA marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package com.itsaky.androidide.ui | ||
| import android.content.Context | ||
| import android.util.AttributeSet | ||
| import androidx.core.widget.NestedScrollView | ||
| import com.google.android.material.navigationrail.NavigationRailView | ||
| class IdeNavigationRailView @JvmOverloads constructor( | ||
| context: Context, | ||
| attrs: AttributeSet? = null, | ||
| defStyleAttr: Int = com.google.android.material.R.attr.navigationRailStyle | ||
| ) : NavigationRailView(context, attrs, defStyleAttr) { | ||
| companion object { | ||
| const val MAX_ITEM_COUNT = 12 | ||
dara-abijo-adfa marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| override fun getMaxItemCount(): Int = MAX_ITEM_COUNT | ||
| override fun onAttachedToWindow() { | ||
| super.onAttachedToWindow() | ||
| enableMenuScrolling() | ||
| } | ||
| private fun enableMenuScrolling() { | ||
| post { | ||
| val menuView = getChildAt(0) ?: return@post | ||
| if (menuView is NestedScrollView) return@post | ||
| removeView(menuView) | ||
| val scroll = NestedScrollView(context).apply { | ||
| isVerticalScrollBarEnabled = false | ||
| addView( | ||
| menuView, | ||
| LayoutParams( | ||
| LayoutParams.WRAP_CONTENT, | ||
| LayoutParams.WRAP_CONTENT | ||
| ) | ||
| ) | ||
| } | ||
| addView( | ||
| scroll, | ||
| LayoutParams( | ||
| LayoutParams.WRAP_CONTENT, | ||
| LayoutParams.MATCH_PARENT | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -49,6 +49,7 @@ import com.itsaky.androidide.actions.sidebar.TerminalSidebarAction | ||
| import com.itsaky.androidide.fragments.sidebar.EditorSidebarFragment | ||
| import com.itsaky.androidide.plugins.extensions.UIExtension | ||
| import com.itsaky.androidide.actions.PluginSidebarActionItem | ||
| import com.itsaky.androidide.actions.SidebarSlotManager | ||
| import com.itsaky.androidide.plugins.manager.core.PluginManager | ||
| import java.lang.ref.WeakReference | ||
| @@ -77,6 +78,9 @@ internal object EditorSidebarActions { | ||
| registry.registerAction(CloseProjectSidebarAction(context, ++order)) | ||
| registry.registerAction(HelpSideBarAction(context, ++order)) | ||
| // Set built-in item count (6 items) for sidebar slot management | ||
| SidebarSlotManager.setBuiltInItemCount(order + 1) | ||
Daniel-ADFA marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // Register plugin sidebar items | ||
| registerPluginSidebarActions(context, registry, ++order) | ||
| } | ||
| @@ -236,13 +240,22 @@ internal object EditorSidebarActions { | ||
| pluginManager.getAllPluginInstances() | ||
| .filterIsInstance<UIExtension>() | ||
| .forEach { plugin -> | ||
| try { | ||
| plugin.getSideMenuItems().forEach { navItem -> | ||
| val action = PluginSidebarActionItem(context, navItem, order++) | ||
| registry.registerAction(action) | ||
| } | ||
| } catch (e: Exception) { | ||
| val pluginId = pluginManager.getPluginIdForInstance(plugin as com.itsaky.androidide.plugins.IPlugin) | ||
| val declaredSlots = SidebarSlotManager.getDeclaredSlots(pluginId ?: "") | ||
| val sideMenuItems = plugin.getSideMenuItems() | ||
| if (sideMenuItems.isEmpty()) return@forEach | ||
| if (sideMenuItems.size > declaredSlots) { | ||
| throw IllegalStateException( | ||
| "Plugin '$pluginId' returned ${sideMenuItems.size} sidebar items " + | ||
| "but only declared $declaredSlots in manifest" | ||
| ) | ||
| } | ||
Daniel-ADFA marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| sideMenuItems.forEach { navItem -> | ||
| val action = PluginSidebarActionItem(context, navItem, order++) | ||
| registry.registerAction(action) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.itsaky.androidide.plugins.services | ||
| interface IdeSidebarService { | ||
| fun getAvailableSidebarSlots(): Int | ||
| fun canAddSidebarItems(count: Int): Boolean | ||
| fun getMaxSidebarItems(): Int | ||
| fun getCurrentSidebarItemCount(): Int | ||
| fun getDeclaredSidebarSlots(): Int | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.