I suspect what you are describing is the intended behaviour. The label position is determined by auxiliary storage which is joined to your features by an unique id field (when you first move a label you are prompted for this field).
As such the auxiliary fields can only store one label location for each feature, and doesn't have any way of setting multiple label locations for multi-part geometries at this stage.
The most straightforward option is to convert the polygon to single part types and use @ahmadhanb's approach to generate an unique id for each part (note: you can't try to do this in Virtual Layer and move the labels - because you can't create auxiliary storage for Virtual Layers)
If you absolutely want to retain multiple labels and are fine with
- only being able to move one label, and
- label locations for all other parts fixed by a geometry generator expression
Then you could use a rather convoluted and limited workaround:
Set up your desired label style then duplicate your layer.
One ('Movable') will have a single movable label and auxiliary storage for label location. The other ('Static') will have static labels.
Make the Static layer invisible.
- Move a label in your Movable layer to generate the auxiliary storage for labels.
- In your Static layer, use the expression at the bottom of this answer in geometry generator under label placement, but replace the layer name and ID field and buffer amount as required.
- Now you can move your labels around in the Movable layer within the buffer amount set in step 4 and the Static label will not show for that part. All other labels will be fixed to each part's centroid.
See example below:
- White dotted line = buffer amount
- Green label = movable
- White label = static (geometry generator)
The expression to use in geometry generator label placement:
collect_geometries(
array_filter(
array_foreach(
array_foreach(
generate_series(1,num_geometries($geometry)),
geometry_n($geometry,@element)),
CASE WHEN intersects(
make_point(
attribute(
get_feature('MOVABLE_LAYER_NAME',
'ID_FIELD',
"ID_FIELD"),
'auxiliary_storage_labeling_positionx'),
attribute(
get_feature('MOVABLE_LAYER_NAME',
'ID_FIELD',
"ID_FIELD"),
'auxiliary_storage_labeling_positiony')),
buffer(@element,0.05)) --change buffer as required
THEN null
ELSE centroid(@element)
END),
@element is not null))