Line up every widget with the icons, as the Pictures widget does
Widgets of other apps used to fill their cells minus Launcher3's widget
padding, which is a few pixels wider than the icons' edges and reaches down
into the room kept for icon labels; on the owner's phone they hung about
13 px past the icons sideways and 40 px below. Now the room a widget keeps in
its cells is the distance from the cell edges to the icon edges, worked out
as IconFrame does for the launcher's own widgets, so every widget's edges
meet the edges of the icons around it.
The one function (FlexeraWidgets.appWidgetPadding) is used for the layout,
for the size the app is told, for the drop animation and through
WidgetSizes for previews. The cells a widget needs are still worked out
with Launcher3's padding, so no widget changes its cell count. A new switch
under Settings, Home screen, Widgets ("Line up with icons", on by default)
turns it off; Lawnchair's Widget padding slider shows only then.
Checked on the emulator against lines drawn at the icon edges: Google
Calendar's widgets meet them within 2 px; Google Clock's sits about 20 px
inside, because it draws an empty margin of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0dbb146c75
commit
f5d7cf07ed
9 files changed
+100
-21
No files matched your search
@@ -97,6 +97,9 @@
|
||||
<!-- Shown after an app was hidden from the app list. %1$s is the app's name -->
|
||||
<string name="flexera_app_hidden">%1$s is hidden</string>
|
||||
<string name="flexera_folder_blur">Blur behind open folders</string>
|
||||
<!-- Home screen settings, Widgets: every widget's edges meet the edges of the icons -->
|
||||
<string name="flexera_align_widgets">Line up with icons</string>
|
||||
<string name="flexera_align_widgets_description">The edges of every widget meet the edges of the icons above, below and beside it</string>
|
||||
<string name="flexera_folder_blur_description">Blurs the home screen while a folder is open</string>
|
||||
<string name="flexera_quick_launch">Quick launch</string>
|
||||
<string name="flexera_quick_launch_description">Swipe on from the first or the last home screen page, as if there were one more, to open an app or do something else. Has no effect while Infinite scrolling is on.</string>
|
||||
|
||||
@@ -5,9 +5,13 @@ import android.content.ComponentName
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.android.launcher3.DeviceProfile
|
||||
import com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR
|
||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView
|
||||
import com.android.launcher3.widget.PendingAppWidgetHostView
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* The widgets that come with the launcher: Pictures ([PictureWidgetProvider]) and Clock
|
||||
@@ -16,7 +20,8 @@ import com.android.launcher3.widget.PendingAppWidgetHostView
|
||||
* Android knows each as an ordinary app widget, so they are added, moved, resized and removed
|
||||
* like any other. What they show is not sent through Android, though: the launcher draws them
|
||||
* itself with a view of its own, which is what lets them line up with the icons ([IconFrame]).
|
||||
* This is where Launcher3's widget code asks about them.
|
||||
* This is where Launcher3's widget code asks about them, and about the room every other widget
|
||||
* keeps in its cells, which by default lines those up with the icons as well.
|
||||
*/
|
||||
object FlexeraWidgets {
|
||||
|
||||
@@ -26,14 +31,54 @@ object FlexeraWidgets {
|
||||
provider == PictureWidgetProvider.componentName || provider == ClockWidgetProvider.componentName
|
||||
|
||||
/**
|
||||
* The room a home screen cell keeps free around a widget. Launcher3 gives every widget the
|
||||
* same ([standard]); the launcher's own take none, because they place what they draw
|
||||
* against the icons of their cells by themselves.
|
||||
* The room a widget's cells keep free around it, on each side; one cell is [cellWidth] by
|
||||
* [cellHeight]. The launcher's own widgets take none, because they place what they draw
|
||||
* against the icons of their cells by themselves ([IconFrame]); every other widget gets
|
||||
* [appWidgetPadding].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun cellInset(child: View, standard: Rect): Rect {
|
||||
val info = child.tag as? LauncherAppWidgetInfo ?: return standard
|
||||
return if (isOwn(info.providerName)) noInset else standard
|
||||
fun cellInset(child: View, profile: DeviceProfile, cellWidth: Int, cellHeight: Int): Rect {
|
||||
val info = child.tag as? LauncherAppWidgetInfo
|
||||
if (info != null && isOwn(info.providerName)) return noInset
|
||||
return appWidgetPadding(profile, cellWidth, cellHeight)
|
||||
}
|
||||
|
||||
/** [appWidgetPadding] for the cells of [profile]'s home screen. */
|
||||
@JvmStatic
|
||||
fun appWidgetPadding(profile: DeviceProfile): Rect {
|
||||
val cell = profile.cellSize
|
||||
return appWidgetPadding(profile, cell.x, cell.y)
|
||||
}
|
||||
|
||||
/**
|
||||
* The room a widget of another app keeps free inside its cells, on each side, where one
|
||||
* cell is [cellWidth] by [cellHeight]. Lined up with the icons (the default), it is the
|
||||
* distance from the cells' edges to the edges of the icons that would sit in them: the
|
||||
* same box the launcher's own widgets draw in ([IconFrame]), so that every widget's edges
|
||||
* meet the edges of the icons above, below and beside it. Otherwise it is Launcher3's own,
|
||||
* which the Widget padding setting scales.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun appWidgetPadding(profile: DeviceProfile, cellWidth: Int, cellHeight: Int): Rect {
|
||||
if (!profile.alignWidgetsWithIcons || cellWidth <= 0 || cellHeight <= 0) {
|
||||
return profile.widgetPadding
|
||||
}
|
||||
// As in IconFrame: the icon is centred sideways in its cell and sits a set distance
|
||||
// below the cell's top; what is seen of it leaves a little air inside its square.
|
||||
val icon = profile.iconSizePx.toFloat()
|
||||
val air = icon * (1f - ICON_VISIBLE_AREA_FACTOR) / 2f
|
||||
val iconTop = if (profile.cellYPaddingPx >= 0) {
|
||||
profile.cellYPaddingPx.toFloat()
|
||||
} else {
|
||||
max(0f, (cellHeight - profile.cellHeightPx) / 2f)
|
||||
}
|
||||
val side = max(0, ((cellWidth - icon) / 2f + air).roundToInt())
|
||||
return Rect(
|
||||
side,
|
||||
max(0, (iconTop + air).roundToInt()),
|
||||
side,
|
||||
max(0, (cellHeight - iconTop - icon + air).roundToInt()),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,7 +41,8 @@ internal class IconFrame private constructor(
|
||||
|
||||
// The cells the widget covers, measured from the corner of the host view. Undoes
|
||||
// what CellLayoutLayoutParams.setup took off them.
|
||||
val inset = FlexeraWidgets.cellInset(host, profile.widgetPadding)
|
||||
val cell = profile.cellSize
|
||||
val inset = FlexeraWidgets.cellInset(host, profile, cell.x, cell.y)
|
||||
val spanLeft = -(lp.leftMargin + inset.left).toFloat()
|
||||
val spanTop = -(lp.topMargin + inset.top).toFloat()
|
||||
val spanWidth = (lp.width + lp.leftMargin + lp.rightMargin + inset.left + inset.right).toFloat()
|
||||
|
||||
@@ -886,6 +886,15 @@ class PreferenceManager2 @Inject constructor(
|
||||
defaultValue = GestureHandlerConfig.NoOp,
|
||||
)
|
||||
|
||||
// Flexera Launcher: whether the edges of every widget meet the edges of the icons in the
|
||||
// rows and columns it covers, as the launcher's own widgets do
|
||||
// (app.flexera.widget.FlexeraWidgets.appWidgetPadding). Read with the grid.
|
||||
val alignWidgetsWithIcons = preference(
|
||||
key = booleanPreferencesKey(name = "flexera_align_widgets_with_icons"),
|
||||
defaultValue = true,
|
||||
onSet = { reloadHelper.reloadGrid() },
|
||||
)
|
||||
|
||||
// Flexera Launcher: whether the home screen behind an open folder is blurred
|
||||
// (app.flexera.home.FolderBlur). Read when a folder opens, so nothing has to be reloaded.
|
||||
val folderBlur = preference(
|
||||
|
||||
@@ -285,13 +285,23 @@ fun HomeScreenPreferences(
|
||||
label = stringResource(id = R.string.force_widget_resize_label),
|
||||
description = stringResource(id = R.string.force_widget_resize_description),
|
||||
)
|
||||
SliderPreference(
|
||||
label = stringResource(id = R.string.widget_padding_label),
|
||||
adapter = prefs2.widgetPaddingFactor.getAdapter(),
|
||||
step = 0.05f,
|
||||
valueRange = 0F..2F,
|
||||
showAsPercentage = true,
|
||||
// Flexera Launcher: widgets lined up with the icons; Lawnchair's padding is for
|
||||
// when they are not (app.flexera.widget.FlexeraWidgets.appWidgetPadding).
|
||||
val alignWidgetsAdapter = prefs2.alignWidgetsWithIcons.getAdapter()
|
||||
SwitchPreference(
|
||||
adapter = alignWidgetsAdapter,
|
||||
label = stringResource(id = R.string.flexera_align_widgets),
|
||||
description = stringResource(id = R.string.flexera_align_widgets_description),
|
||||
)
|
||||
ExpandAndShrink(visible = !alignWidgetsAdapter.state.value) {
|
||||
SliderPreference(
|
||||
label = stringResource(id = R.string.widget_padding_label),
|
||||
adapter = prefs2.widgetPaddingFactor.getAdapter(),
|
||||
step = 0.05f,
|
||||
valueRange = 0F..2F,
|
||||
showAsPercentage = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +349,8 @@ public class DeviceProfile {
|
||||
private float workspacePaddingHorizontalFactor;
|
||||
private float workspacePaddingVerticalFactor;
|
||||
private float widgetPaddingFactor;
|
||||
// Flexera Launcher: widgets lined up with the icons (app.flexera.widget.FlexeraWidgets).
|
||||
public boolean alignWidgetsWithIcons;
|
||||
private float drawerPaddingTopFactor;
|
||||
private PreferenceManager2 preferenceManager2 = null;
|
||||
|
||||
@@ -373,6 +375,8 @@ public class DeviceProfile {
|
||||
.firstCached(preferenceManager2.getWorkspacePaddingVerticalFactor()), 0f, 2f);
|
||||
widgetPaddingFactor = Utilities.boundToRange(PreferenceCacheExtensionsKt
|
||||
.firstCached(preferenceManager2.getWidgetPaddingFactor()), 0f, 2f);
|
||||
alignWidgetsWithIcons = PreferenceCacheExtensionsKt
|
||||
.firstCached(preferenceManager2.getAlignWidgetsWithIcons());
|
||||
drawerPaddingTopFactor = Utilities.boundToRange(PreferenceCacheExtensionsKt
|
||||
.firstCached(preferenceManager2.getDrawerPaddingTopFactor()), 1f, 2f);
|
||||
this.inv = inv;
|
||||
|
||||
@@ -151,9 +151,10 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.
|
||||
final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag());
|
||||
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
|
||||
appWidgetScale.x, appWidgetScale.y, mBorderSpace,
|
||||
// Flexera Launcher: the launcher's own widgets take their cells whole.
|
||||
app.flexera.widget.FlexeraWidgets.cellInset(child,
|
||||
profile.widgetPadding));
|
||||
// Flexera Launcher: the launcher's own widgets take their cells whole;
|
||||
// the others are lined up with the icons.
|
||||
app.flexera.widget.FlexeraWidgets.cellInset(child, profile,
|
||||
mCellWidth, mCellHeight));
|
||||
} else {
|
||||
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
|
||||
mBorderSpace);
|
||||
@@ -179,8 +180,10 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.
|
||||
final PointF appWidgetScale = dp.getAppWidgetScale((ItemInfo) child.getTag());
|
||||
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
|
||||
appWidgetScale.x, appWidgetScale.y, mBorderSpace,
|
||||
// Flexera Launcher: the launcher's own widgets take their cells whole.
|
||||
app.flexera.widget.FlexeraWidgets.cellInset(child, dp.widgetPadding));
|
||||
// Flexera Launcher: the launcher's own widgets take their cells whole;
|
||||
// the others are lined up with the icons.
|
||||
app.flexera.widget.FlexeraWidgets.cellInset(child, dp,
|
||||
mCellWidth, mCellHeight));
|
||||
} else if (isChildQsb(child)) {
|
||||
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
|
||||
mBorderSpace);
|
||||
|
||||
@@ -3300,7 +3300,10 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
|
||||
DeviceProfile profile = mLauncher.getDeviceProfile();
|
||||
if (finalView instanceof NavigableAppWidgetHostView) {
|
||||
Rect widgetPadding = profile.widgetPadding;
|
||||
// Flexera Launcher: the room this widget keeps in its cells.
|
||||
Point cell = profile.getCellSize();
|
||||
Rect widgetPadding = app.flexera.widget.FlexeraWidgets.cellInset(
|
||||
finalView, profile, cell.x, cell.y);
|
||||
r.left -= widgetPadding.left;
|
||||
r.right += widgetPadding.right;
|
||||
r.top -= widgetPadding.top;
|
||||
|
||||
@@ -61,7 +61,8 @@ public final class WidgetSizes {
|
||||
final int vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacePx.y;
|
||||
|
||||
Point cellSize = profile.getCellSize();
|
||||
Rect padding = profile.widgetPadding;
|
||||
// Flexera Launcher: the size the widget really gets, lined up with the icons.
|
||||
Rect padding = app.flexera.widget.FlexeraWidgets.appWidgetPadding(profile);
|
||||
|
||||
return new Size(
|
||||
(spanX * cellSize.x) + hBorderSpacing - padding.left - padding.right,
|
||||
|
||||
Reference in new issue
Block a user