Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -32,6 +32,7 @@ import com.itsaky.androidide.plugins.PluginContext
import com.itsaky.androidide.plugins.aiagentgemini.plugin.GeminiPlugin
import com.itsaky.androidide.plugins.aiagentgemini.R
import com.itsaky.androidide.plugins.aiagentgemini.ui.SecretRevealController
import com.itsaky.androidide.plugins.aiagentgemini.ui.applyPaneStyling
import com.itsaky.androidide.plugins.base.PluginFragmentHelper
import com.itsaky.androidide.plugins.security.KeystoreSecretStore
import com.itsaky.androidide.plugins.services.IdeTooltipService
Expand All @@ -40,6 +41,14 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

/** The pane's secondary actions, drawn outlined; each section's Save stays filled. */
private val OUTLINED_BUTTON_IDS = setOf(
R.id.btn_clear_api_key,
R.id.btn_edit_api_key,
R.id.btn_get_free_key,
R.id.btn_refresh_models,
)

/**
* This backend's settings pane, mounted by whichever screen offers a backend selector.
*
Expand Down Expand Up @@ -108,6 +117,7 @@ class GeminiSettingsFragment : Fragment() {
GeminiSettingsViewModelFactory { GeminiPlugin.getContext() }
)[GeminiSettingsViewModel::class.java]

view.applyPaneStyling(OUTLINED_BUTTON_IDS)
setupApiKeyUi(view)
setupModelPicker(view, chatModelPicker())
setupModelPicker(view, embeddingModelPicker())
Expand Down Expand Up @@ -580,6 +590,7 @@ class GeminiSettingsFragment : Fragment() {
}.isSuccess
}


/**
* Put the dropdown chevron on [box]'s end icon.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.itsaky.androidide.plugins.aiagentgemini.ui

import android.content.res.ColorStateList
import android.graphics.Color
import android.view.View
import android.view.ViewGroup
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import com.google.android.material.button.MaterialButton
import com.google.android.material.divider.MaterialDivider
import com.google.android.material.textfield.TextInputLayout
import com.itsaky.androidide.plugins.aiagentgemini.R

/** How much a pane button stands out: one filled action per section, the rest outlined. */
private enum class ButtonEmphasis { FILLED, OUTLINED }

/**
* Gives every Material button, text field and divider under [this] its Material 3 colours, outline
* and ripple in code. The styles' `app:` items are dropped inside the host, so XML alone leaves
* these controls on the host theme's values.
*
* @param outlinedButtonIds the buttons that are secondary actions; every other button is filled.
*/
internal fun View.applyPaneStyling(outlinedButtonIds: Set<Int>) {
when (this) {
is MaterialButton -> applyEmphasis(
if (id in outlinedButtonIds) ButtonEmphasis.OUTLINED else ButtonEmphasis.FILLED
)
is TextInputLayout -> applyOutline()
is MaterialDivider -> applyHairline()
}
if (this is ViewGroup) {
for (i in 0 until childCount) getChildAt(i).applyPaneStyling(outlinedButtonIds)
}
}

/** Container, label, icon, border and ripple for [emphasis], each with its disabled state. */
private fun MaterialButton.applyEmphasis(emphasis: ButtonEmphasis) {
val filled = emphasis == ButtonEmphasis.FILLED
val content = colors(
if (filled) R.color.plugin_button_filled_content else R.color.plugin_button_outlined_content
)
backgroundTintList = if (filled) {
colors(R.color.plugin_button_filled_container)
} else {
ColorStateList.valueOf(Color.TRANSPARENT)
}
setTextColor(content)
iconTint = content
rippleColor = colors(
if (filled) R.color.plugin_button_filled_ripple else R.color.plugin_button_outlined_ripple
)
strokeColor = colors(R.color.plugin_button_outlined_stroke)
strokeWidth = if (filled) 0 else resources.getDimensionPixelSize(R.dimen.button_stroke_width)
cornerRadius = resources.getDimensionPixelSize(R.dimen.radius_md)
}

/** Outline, corners, hint and end icon of an outlined-box field. */
private fun TextInputLayout.applyOutline() {
setBoxStrokeColorStateList(colors(R.color.plugin_box_stroke))
setBoxStrokeErrorColor(colors(R.color.plugin_error))
val radius = resources.getDimension(R.dimen.radius_md)
setBoxCornerRadii(radius, radius, radius, radius)
val hint = colors(R.color.plugin_text_muted)
defaultHintTextColor = hint
hintTextColor = hint
setEndIconTintList(colors(R.color.plugin_on_surface_variant))
}

private fun MaterialDivider.applyHairline() {
setDividerColorResource(R.color.plugin_outline_variant)
setDividerThicknessResource(R.dimen.divider_thickness)
}

/** Resolved against this view's context, which carries the plugin's resources. */
private fun View.colors(@ColorRes id: Int): ColorStateList =
requireNotNull(ContextCompat.getColorStateList(context, id))
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Text field outline. Focus uses the neutral accent, not the plugin hue, to match the buttons. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.38" android:color="@color/plugin_on_surface" android:state_enabled="false" />
<item android:color="@color/plugin_secondary" android:state_focused="true" />
<item android:color="@color/plugin_on_surface" android:state_hovered="true" />
<item android:color="@color/plugin_outline" />
</selector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Filled button container: the neutral accent, and M3's 12% on-surface wash while disabled. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.12" android:color="@color/plugin_on_surface" android:state_enabled="false" />
<item android:color="@color/plugin_secondary" />
</selector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Filled button label and icon: on-accent, and M3's 38% on-surface while disabled. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.38" android:color="@color/plugin_on_surface" android:state_enabled="false" />
<item android:color="@color/plugin_on_secondary" />
</selector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Filled button ripple, drawn in the label colour so it shows on the dark container. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.12" android:color="@color/plugin_on_secondary" android:state_pressed="true" />
<item android:alpha="0.12" android:color="@color/plugin_on_secondary" android:state_focused="true" />
<item android:alpha="0.08" android:color="@color/plugin_on_secondary" android:state_hovered="true" />
<item android:alpha="0.12" android:color="@color/plugin_on_secondary" />
</selector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Outlined button label and icon: on-surface, and M3's 38% while disabled. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.38" android:color="@color/plugin_on_surface" android:state_enabled="false" />
<item android:color="@color/plugin_on_surface" />
</selector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Outlined button ripple, drawn in the label colour over the transparent container. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.12" android:color="@color/plugin_on_surface" android:state_pressed="true" />
<item android:alpha="0.12" android:color="@color/plugin_on_surface" android:state_focused="true" />
<item android:alpha="0.08" android:color="@color/plugin_on_surface" android:state_hovered="true" />
<item android:alpha="0.12" android:color="@color/plugin_on_surface" />
</selector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Outlined button border: the outline role, and M3's 12% on-surface while disabled. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.12" android:color="@color/plugin_on_surface" android:state_enabled="false" />
<item android:color="@color/plugin_outline" />
</selector>
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_clear_api_key"
style="@style/PluginButton.Filled"
style="@style/PluginButton.Outlined"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
Expand All @@ -84,7 +84,7 @@

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_edit_api_key"
style="@style/PluginButton.Filled"
style="@style/PluginButton.Outlined"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/space_sm"
Expand All @@ -104,12 +104,21 @@

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_get_free_key"
style="@style/PluginButton.Filled"
style="@style/PluginButton.Outlined"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_sm"
android:text="@string/btn_get_free_key" />

<!-- Divider: ends the key section before the models. The Model header's top margin below
mirrors this one's, so the line sits centred in the gap. Colour and thickness are set in
code: app: attributes are dropped inside the host. -->
<com.google.android.material.divider.MaterialDivider
android:id="@+id/gemini_models_divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_xl" />

<!-- Model. One dropdown, the same control the OpenAI pane uses: the catalog is whatever the
key can reach, so the list is the only way to change it. The chevron is not declared here —
declared as app:endIconDrawable it draws blank inside the host, so
Expand Down Expand Up @@ -191,7 +200,7 @@
<!-- One Refresh for both pickers: a single catalog walk fills both lists. -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_refresh_models"
style="@style/PluginButton.Filled"
style="@style/PluginButton.Outlined"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_md"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,4 @@
<color name="plugin_text_muted">#A3A3A3</color>
<color name="plugin_text_subtle">#525252</color>

<color name="plugin_ripple">#1FFAFAFA</color>
</resources>
1 change: 0 additions & 1 deletion plugins/AI-Agent-Gemini/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,4 @@
<color name="plugin_text_muted">#737373</color>
<color name="plugin_text_subtle">#A3A3A3</color>

<color name="plugin_ripple">#1F0A0A0A</color>
</resources>
3 changes: 3 additions & 0 deletions plugins/AI-Agent-Gemini/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<dimen name="space_lg">16dp</dimen>
<dimen name="space_xl">24dp</dimen>

<dimen name="divider_thickness">1dp</dimen>
<dimen name="button_stroke_width">1dp</dimen>

<dimen name="radius_md">8dp</dimen>

<dimen name="text_micro">11sp</dimen>
Expand Down
28 changes: 21 additions & 7 deletions plugins/AI-Agent-Gemini/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,33 @@
by choice, matching the IDE's own grey accent rather than this plugin's orange, on a screen
whose chrome belongs to AI Core. Light/dark comes from values-night/colors.xml.

One style for every button on the pane: a secondary action that styled itself differently
is exactly what broke on the OpenAI pane, and the hierarchy is not worth a second way to
get contrast wrong. -->
Filled for the one primary action in a section, outlined for the rest, per Material 3's
button hierarchy. Both pin direct colours, so neither resolves a role against the host.
The app: items here are dropped inside the host; ui/PaneStyling.kt applies them in code. -->
<style name="PluginButton.Filled" parent="Widget.Material3.Button">
<item name="android:textAllCaps">false</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:letterSpacing">0</item>
<item name="cornerRadius">@dimen/radius_md</item>
<item name="android:minHeight">@dimen/touch_target_min</item>
<item name="android:textColor">@color/plugin_on_secondary</item>
<item name="backgroundTint">@color/plugin_secondary</item>
<item name="iconTint">@color/plugin_on_secondary</item>
<item name="rippleColor">@color/plugin_ripple</item>
<item name="android:textColor">@color/plugin_button_filled_content</item>
<item name="backgroundTint">@color/plugin_button_filled_container</item>
<item name="iconTint">@color/plugin_button_filled_content</item>
<item name="rippleColor">@color/plugin_button_filled_ripple</item>
</style>

<style name="PluginButton.Outlined" parent="Widget.Material3.Button.OutlinedButton">
<item name="android:textAllCaps">false</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:letterSpacing">0</item>
<item name="cornerRadius">@dimen/radius_md</item>
<item name="android:minHeight">@dimen/touch_target_min</item>
<item name="android:textColor">@color/plugin_button_outlined_content</item>
<item name="backgroundTint">@android:color/transparent</item>
<item name="iconTint">@color/plugin_button_outlined_content</item>
<item name="strokeColor">@color/plugin_button_outlined_stroke</item>
<item name="strokeWidth">@dimen/button_stroke_width</item>
<item name="rippleColor">@color/plugin_button_outlined_ripple</item>
</style>

<!-- Inputs -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.itsaky.androidide.plugins.PluginContext
import com.itsaky.androidide.plugins.aiagentopenai.R
import com.itsaky.androidide.plugins.aiagentopenai.plugin.OpenAiPlugin
import com.itsaky.androidide.plugins.aiagentopenai.ui.SecretRevealController
import com.itsaky.androidide.plugins.aiagentopenai.ui.applyPaneStyling
import com.itsaky.androidide.plugins.base.PluginFragmentHelper
import com.itsaky.androidide.plugins.security.KeystoreSecretStore
import com.itsaky.androidide.plugins.services.IdeTooltipService
Expand All @@ -42,6 +43,14 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

/** The pane's secondary actions, drawn outlined; each section's Save stays filled. */
private val OUTLINED_BUTTON_IDS = setOf(
R.id.btn_clear_api_key,
R.id.btn_edit_api_key,
R.id.btn_get_key,
R.id.btn_test_connection,
)

/**
* This backend's settings pane, mounted by whichever screen offers a backend selector.
*
Expand Down Expand Up @@ -112,6 +121,7 @@ class OpenAiSettingsFragment : Fragment() {

// The key section publishes onServerChanged, so it is built before the server section that
// fires it, and before the first call below that dresses the pane for the saved server.
view.applyPaneStyling(OUTLINED_BUTTON_IDS)
setupApiKeyUi(view)
setupServerUi(view)
setupModelPicker(view, chatModelPicker())
Expand Down Expand Up @@ -199,6 +209,7 @@ class OpenAiSettingsFragment : Fragment() {
target.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
}


/**
* Put the dropdown chevron on [box]'s end icon.
*
Expand Down
Loading
Loading